FilenameUtils.getName function analysis
最近、org.apache.commons.io.FilenameUtils#getName メソッドを使用しました。このメソッドはファイルパスを渡してファイル名を取得できます。
ソースコードを簡単に見てみたところ、複雑ではありませんが、私自身のアイデアとは若干異なり、学ぶべき価値があります。この記事ではそれを簡単に分析します。
ここに画像の説明を挿入
2. ソースコード分析
org.apache.commons.io.FilenameUtils#getName
/**
* Gets the name minus the path from a full fileName.
*
* This method will handle a file in either Unix or Windows format.
* The text after the last forward or backslash is returned.
*
* a/b/c.txt --> c.txt
* a.txt --> a.txt
* a/b/c --> c
* a/b/c/ --> ""
*
*
* The output will be the same irrespective of the machine that the code is running on.
*
* @param fileName the fileName to query, null returns null
* @return the name of the file without the path, or an empty string if none exists.
* Null bytes inside string will be removed
*/
public static String getName(final String fileName) {
// Pass in null and return null directly
if (fileName == null) {
return null;
}
// NonNul check
requireNonNullChars(fileName);
// find the last delimiter
final int index = indexOfLastSeparator(fileName);
// steal from the last delimiter to the end
return fileName.substring(index + 1);
}
2.1 質問 1: なぜ NonNul チェックが必要なのか?
2.1.1 どのようにチェックするか?
org.apache.commons.io.FilenameUtils#requireNonNullChars
/**
* Checks the input for null bytes, a sign of unsanitized data being passed to to file level functions.
*
* This may be used for poison byte attacks.
*
* @param path the path to check
*/
private static void requireNonNullChars(final String path) {
if (path. indexOf(0) >= 0) {
throw new IllegalArgumentException("Null byte present in file/path name. There are no "
+ "known legitimate use cases for such data, but several injection attacks may use it");
}
}
java.lang.String#indexOf(int) ソースコード:
/**
* Returns the index within this string of the first occurrence of
* the specified character. If a character with value
* {@code ch} occurs in the character sequence represented by
* this {@code String} object, then the index (in Unicode
* code units) of the first such occurrence is returned. For
* values of {@code ch} in the range from 0 to 0xFFFF
* (inclusive), this is the smallest value k such that:
*
* this.charAt(k) == ch
*
* is true. For other values of {@code ch}, it is the
* smallest value k such that:
*
* this.codePointAt(k) == ch
*
* is true. In either case, if no such character occurs in this
* string, then {@code -1} is returned.
*
* @param ch a character (Unicode code point).
* @return the index of the first occurrence of the character in the
* character sequence represented by this object, or
* {@code -1} if the character does not occur.
*/
public int indexOf(int ch) {
return indexOf(ch, 0);
}
indexOf(0) の目的は ASCII コードが 0 の文字の位置を見つけることで、見つかった場合は IllegalArgumentException がスローされます。
ASCII 比較表を検索したところ、0 の ASCII 値は制御文字 NUT を表し、通常のファイル名に含まれるべき文字ではないことがわかりました。
ここに画像の説明を挿入
2.1.2 なぜこの検査が行われるのか?
null バイトは値が 0 のバイトです(16 進数では 0x00 など)。
null バイトに関連するセキュリティ脆弱性があります。
C 言語では null バイトは文字列終端子として使用されますが、他の言語(Java、PHP など)にはこの文字列終端子がありません。
たとえば、Java Web プロジェクトではユーザーが .jpg 形式の画像のみをアップロードできるように制限されていますが、この脆弱性を利用して .jsp ファイルをアップロードできます。
一部のプログラミング言語ではファイル名に ·· を使用できません
したがって、このチェックは必要です。
コード例:
package org.example;
import org.apache.commons.io.FilenameUtils;
public class FilenameDemo {
public static void main(String[] args) {
System.out.println( FilenameUtils.getName(filename));
}
}
エラーメッセージ:
Exception in thread "main" java.lang.IllegalArgumentException: Null byte present in file/path name. There are no known legitimate use cases for such data, but several injection attacks may use it
at org.apache.commons.io.FilenameUtils.requireNonNullChars(FilenameUtils.java:998)
at org.apache.commons.io.FilenameUtils.getName(FilenameUtils.java:984)
at org.example.FilenameDemo.main(FilenameDemo.java:8)
検証を削除した場合:
package org.example;
import org.apache.commons.io.FilenameUtils;
public class FilenameDemo {
public static void main(String[] args) {
// do not add validation
String name = getName(filename);
// Get the extension name
String extension = FilenameUtils. getExtension(name);
System.out.println(extension);
}
public static String getName(final String fileName) {
if (fileName == null) {
return null;
}
final int index = FilenameUtils. indexOfLastSeparator(fileName);
return fileName.substring(index + 1);
}
}
Java は拡張子を jpg として認識します
興味がある方は、C 言語で lse という名前のファイルを使用してみてください。if (delta > 0) {
// We don't really know how many new threads are "needed".
// As a heuristic, prestart enough new workers (up to new
// core size) to handle the current number of tasks in
// queue, but stop if queue becomes empty while doing so.
int k = Math. min(delta, workQueue. size());
while (k-- > 0 && addWorker(null, true)) {
if (workQueue. isEmpty())
break;
}
}
}
(3) 日々の業務開発では、関連ドキュメントや設定ページへのリンクをコメントに含めることを強くお勧めします。これは後のメンテナンスに大いに役立ちます。
例:
/**
* certain function
*
* Related documents:
* Design Document
* Three-party API address
*/
public void demo(){
// omitted
}
(4) ユーティリティクラスの場合、一般的な入力に対応する出力を提供することを検討できます。
例:org.apache.commons.lang3.StringUtils#center(java.lang.String, int, char)
/**
*
Centers a String in a larger String of size {@code size}.
* Uses a supplied character as the value to pad the String with.
*
*
If the size is less than the String length, the String is returned.
* A {@code null} String returns {@code null}.
* A negative size is treated as zero.
*
*
* StringUtils. center(null, *, *) = null
* StringUtils. center("", 4, ' ') = " "
* StringUtils. center("ab", -1, ' ') = "ab"
* StringUtils. center("ab", 4, ' ') = " ab "
* StringUtils. center("abcd", 2, ' ') = "abcd"
* StringUtils. center("a", 4, ' ') = " a "
* StringUtils. center("a", 4, 'y') = "yayy"
*
*
* @param str the String to center, may be null
* @param size the int size of new String, negative treated as zero
* @param padChar the character to pad the new String with
* @return centered String, {@code null} if null String input
* @since 2.0
*/
public static String center(String str, final int size, final char padChar) {
if (str == null || size <= 0) {
return str;
}
final int strLen = str. length();
final int pads = size - strLen;
if (pads <= 0) {
return str;
}
str = leftPad(str, strLen + pads / 2, padChar);
str = rightPad(str, size, padChar);
return str;
}
(5) 非推奨のメソッドの場合、非推奨の理由を示し、代替案を提示する必要があります。
例:java.security.Signature#setParameter(java.lang.String, java.lang.Object)
/**
* omitted part
*
* @see #getParameter
*
* @deprecated Use
* {@link #setParameter(java.security.spec.AlgorithmParameterSpec)
* setParameter}.
*/
@Deprecated
public final void setParameter(String param, Object value)
throws InvalidParameterException {
engineSetParameter(param, value);
}
4. まとめ
多くの優れたオープンソースプロジェクトのコード設計は非常に厳密で、シンプルなコードにも慎重な思考が含まれていることがよくあります。
時間があるときは、いくつかの優れたオープンソースプロジェクトを見てみると良いでしょう。シンプルなものから始めて、自分で書くならどのように実装するかをまず考え、その後作成者の実装アイデアと比較すると、より多くの発見があります。
普段ソースコードを見るときのポイントは、ソースコードがなぜこのように設計されているのかを理解することです。
Related Articles
-
A detailed explanation of Hadoop core architecture HDFS
Knowledge Base Team
-
What Does IOT Mean
Knowledge Base Team
-
6 Optional Technologies for Data Storage
Knowledge Base Team
-
What Is Blockchain Technology
Knowledge Base Team
Explore More Special Offers
-
Short Message Service(SMS) & Mail Service
50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00
