Java - SQL enjeksiyonunu önlemek için dizeden çıkış


146

Java'da bazı anti sql enjeksiyonu yerleştirmeye çalışıyorum ve "replaceAll" dizge işlevi ile çalışmayı çok zor buluyorum. Sonuçta ben mevcut herhangi dönüştürür bir işlev gerekir \için \\herhangi "üzere \", herhangi 'etmek \'ve herhangi \netmek \\nböylece dize MySQL SQL enjeksiyonu tarafından değerlendirilir zaman olduğu engellenecektir.

Üzerinde çalıştığım bazı kodları aradım ve tüm \\\\\\\\\\\işlevler gözlerimi çıldırtıyor. Bunun bir örneği olan olursa çok memnun olurum.


1
Tamam, PreparedStatements'ın gitmenin yolu olduğu sonucuna vardım, ancak mevcut hedeflere dayanarak başlangıçta planlandığı gibi ilerlemem gerekiyor ve şimdilik bir filtre koymalıyım ve mevcut kilometre taşına ulaşıldığında yapabilirim geri dönün ve hazırlanmış ifade için veritabanını yeniden düzenleyin. Bu arada, momentumu sürdürmek için, Java verilen MySQL için yukarıdaki karakterlerden etkili bir şekilde kaçmak için birinin bir çözümü var mı ve bu düzenli ifade sistemi, gereken kaçış sayısını hesaplamak için mutlak bir acıdır ....
Scott Bonner

2
Tüm SQL ifadeleri parametreleştirilemez, örneğin "SET ROLE role_name" veya "LISTEN channel_name"
Neil McGuigan

1
@NeilMcGuigan Evet. Parametrelendirmeye çalıştığınız kısım aslında DML olsa da, çoğu sürücü aynı zamanda CREATE VIEW myview AS SELECT * FROM mytable WHERE col = ?ana ifade bir DDL ifadesi olduğu için parametreleştirmeyi reddedecektir .
Nadiren 'Monica Nerede' Needy

Yanıtlar:


251

PreparedStatements, SQL enjeksiyonunu imkansız hale getirdiği için en iyi yoldur. Aşağıda, kullanıcının girdisini parametre olarak alan basit bir örnek verilmiştir:

public insertUser(String name, String email) {
   Connection conn = null;
   PreparedStatement stmt = null;
   try {
      conn = setupTheDatabaseConnectionSomehow();
      stmt = conn.prepareStatement("INSERT INTO person (name, email) values (?, ?)");
      stmt.setString(1, name);
      stmt.setString(2, email);
      stmt.executeUpdate();
   }
   finally {
      try {
         if (stmt != null) { stmt.close(); }
      }
      catch (Exception e) {
         // log this error
      }
      try {
         if (conn != null) { conn.close(); }
      }
      catch (Exception e) {
         // log this error
      }
   }
}

İsim ve e-postada hangi karakterler olursa olsun, bu karakterler doğrudan veri tabanına yerleştirilecektir. INSERT ifadesini hiçbir şekilde etkilemezler.

Farklı veri türleri için farklı küme yöntemleri vardır - hangisini kullanacağınız, veritabanı alanlarınızın ne olduğuna bağlıdır. Örneğin, veritabanında bir INTEGER sütununuz varsa, bir setIntyöntem kullanmalısınız . PreparedStatement belgeleri , verilerin ayarlanması ve alınması için kullanılabilen tüm farklı yöntemleri listeler.


1
bu yöntem aracılığıyla her parametreyi bir dizge olarak ele alabilir ve yine de güvende olabilir misiniz? Tüm veritabanı katmanını yeniden inşa etmek zorunda kalmadan mevcut mimarimi güvenli olacak şekilde güncellemenin bir yolunu bulmaya çalışıyorum ...
Scott Bonner

1
Tüm dinamik SQL sadece dizelerden ibarettir, bu yüzden sorulacak soru bu değildir. PrepareStatement'a aşina değilim, bu yüzden asıl soru, daha sonra ExecuteUpdate ile yürütülebilecek parametreli bir sorgu oluşturmasıdır. Eğer evet ise, bu iyi. Hayır ise, sorunu gizlemektir ve veritabanı katmanını yeniden tasarlamak dışında herhangi bir güvenli seçeneğiniz olmayabilir. SQL enjeksiyonu ile uğraşmak, baştan tasarlamanız gereken şeylerden biridir; daha sonra kolayca ekleyebileceğiniz bir şey değil.
Cylon Cat

2
Bir INTEGER alanına ekliyorsanız, bir 'setInt' kullanmak isteyeceksiniz. Benzer şekilde, diğer sayısal veritabanı alanları diğer ayarlayıcıları kullanır. Tüm ayarlayıcı türlerini listeleyen PreparedStatement belgelerine bir bağlantı gönderdim.
Kaleb Brasee

2
Yes Cylon, PreparedStatements parametreleştirilmiş sorgular oluşturur.
Kaleb Brasee

2
@Kaleb Brasee, teşekkürler. Bunu bilmek güzel. Araçlar her ortamda farklıdır, ancak parametreleştirilmiş sorgulara ulaşmak temel cevaptır.
Cylon Cat

47

SQL enjeksiyonunu önlemenin tek yolu parametreli SQL kullanmaktır. Geçimini sağlamak için SQL'i hackleyen insanlardan daha akıllı bir filtre oluşturmak mümkün değildir.

Bu nedenle tüm girdiler, güncellemeler ve where cümleleri için parametreleri kullanın. Dinamik SQL, bilgisayar korsanları için açık bir kapıdır ve depolanmış prosedürlerde dinamik SQL içerir. Parametrelendirme, parametrelendirme, parametrelendirme.


11
Ve parametreli SQL bile% 100 garanti değildir. Ama bu çok iyi bir başlangıç.
duffymo

2
@duffymo, hiçbir şeyin% 100 güvenli olmadığını kabul ediyorum. Parametreli SQL ile bile çalışacak bir SQL enjeksiyonu örneğiniz var mı?
Cylon Cat

3
@Cylon Cat: Elbette, bir SQL parçası (@WhereClause veya @tableName gibi) parametre olarak iletildiğinde, SQL'e birleştirildiğinde ve dinamik olarak çalıştırıldığında. Kullanıcıların kodunuzu yazmasına izin verdiğinizde SQL yerleştirme gerçekleşir. Kodlarını bir parametre olarak yakalayıp yakalamanız önemli değildir.
Steve Kass

16
BTW, bundan neden daha fazla bahsedilmediğini bilmiyorum ama PreparedStatements ile çalışmak da çok daha kolay ve çok daha okunaklı. Tek başına bu, muhtemelen onları bilen her programcı için varsayılan yapar.
Edan Maor

3
Bazı veritabanları için PreparedStatements oluşturmanın ÇOK pahalı olduğunu lütfen unutmayın, bu nedenle çoğunu yapmanız gerekiyorsa, her iki türü de ölçün.
Thorbjørn Ravn Andersen

37

Savunma Seçeneği 1'i gerçekten kullanamıyorsanız : Hazırlanmış İfadeler (Parametreli Sorgular) veya Savunma Seçeneği 2: Saklanan Yordamlar , kendi aracınızı oluşturmayın, OWASP Kurumsal Güvenlik API'sini kullanın . Gönderen OWASP ESAPI Google Code üzerinde barındırılan:

Kendi güvenlik kontrollerinizi yazmayın! Her web uygulaması veya web hizmeti için güvenlik kontrolleri geliştirmek söz konusu olduğunda tekerleği yeniden keşfetmek, zamanın boşa harcanmasına ve büyük güvenlik açıklarına yol açar. OWASP Kurumsal Güvenlik API (ESAPI) Araç Takımları, yazılım geliştiricilerin güvenlikle ilgili tasarım ve uygulama kusurlarına karşı korunmalarına yardımcı olur.

Daha fazla ayrıntı için bkz . Java'da SQL Enjeksiyonunu Önleme ve SQL Enjeksiyonu Önleme Hile Sayfası .

Savunma Seçeneği 3: OWASP ESAPI projesini tanıtan Kullanıcı Tarafından Sağlanan Tüm Girişlerden Kaçınma) konusuna özellikle dikkat edin .


4
ESAPI bugün itibariyle feshedilmiş görünüyor. AWS'de SQL enjeksiyonuna, XSS'ye vb. Karşı yardımcı olabilecek WAF var bu noktada başka alternatifler var mı?
ChrisOdney

@ChrisOdney A WAF kolayca atlanabilir. Çoğu Çerçeve, parametrelerden kendi başlarına otomatik olarak çıktıkları kendi SQL Enjeksiyon engellemesini zaten uygulamaktadır. Eski projeler için alternatifler: owasp.org/index.php/…
Javan R.

19

(Bu, OP'nin orijinal soru altındaki yorumuna cevaptır; PreparedStatement'ın bu iş için normal ifadeler değil araç olduğuna tamamen katılıyorum.)

Derken \n, sıralamayı demek \+ nya da gerçek satır besleme karakteri? \+ İse n, görev oldukça basittir:

s = s.replaceAll("['\"\\\\]", "\\\\$0");

Girişte bir ters eğik çizgiyi eşleştirmek için, normal ifade dizesine dört tane koyarsınız. Çıktıya bir ters eğik çizgi koymak için, bunların dördünü yeni dizeye koyarsınız. Bu, normal ifadeleri ve değiştirmeleri Java Dize değişmezleri biçiminde oluşturduğunuzu varsayar. Bunları başka bir yolla oluşturursanız (örneğin, bir dosyadan okuyarak), tüm bu çift kaçışları yapmanız gerekmez.

Girişte bir satır besleme karakteriniz varsa ve onu bir çıkış dizisi ile değiştirmek istiyorsanız, bununla girişin üzerinden ikinci bir geçiş yapabilirsiniz:

s = s.replaceAll("\n", "\\\\n");

Ya da belki iki ters eğik çizgi istiyorsunuz (Bu konuda çok net değilim):

s = s.replaceAll("\n", "\\\\\\\\n");

1
Yorumunuz için teşekkürler, tüm karakterleri bir arada yapma şeklinizi beğendim, her biri için daha az düzenli ifade yolu ile her birini değiştirmeyi düşünüyordum ... Bu sorunun cevabını şimdi nasıl atayacağımdan emin değilim . Cevap nihayetinde PreparedStatements, ancak şu anki amacım için cevabınız ihtiyacım olan cevap, daha önce hazırlanan ifadelerden birine cevap verirsem üzülür müsünüz veya cevabı bir çift arasında paylaşmanın bir yolu var mı?
Scott Bonner

1
Bu sadece geçici bir kludge olduğundan, devam edin ve PreparedStatement yanıtlarından birini kabul edin.
Alan Moore

12

PreparedStatements çoğu durumda başvurmanın yoludur, ancak her durumda değil. Bazen kendinizi bir sorgunun veya bir kısmının daha sonra kullanılmak üzere bir dizge olarak oluşturulması ve saklanması gereken bir durumda bulacaksınız. Check out SQL Enjeksiyon Önleme Hile Sheet üzerinde OWASP Sitesi farklı programlama dillerinde daha ayrıntılı bilgi ve API'ler için.


1
OWASP hile sayfaları GitHub'a taşındı. SQL Enjeksiyon hile sayfası şimdi burada: github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/…
theferrit32

9

Bir SQL enjeksiyonuna neden olabilecek metni kaldırmak için normal bir ifadenin kullanılması, SQL ifadesi veritabanına a Statementyerine a yoluyla gönderiliyormuş gibi sesler çıkarıyor PreparedStatement.

İlk etapta bir SQL enjeksiyonunu önlemenin en kolay yollarından biri PreparedStatement, veritabanına gönderilecek bir SQL ifadesi oluşturmak için dize birleştirmelerine dayanmayan, yer tutucular kullanarak bir SQL ifadesine yer tutacak verileri kabul eden a kullanmaktır.

Daha fazla bilgi için Hazırlanan Tabloların Kullanımı gelen Java Tutorials başlamak için iyi bir yer olurdu.


9

Hazırlanmış İfadeler en iyi çözümdür, ancak bunu gerçekten elle yapmanız gerekiyorsa StringEscapeUtils, Apache Commons-Lang kitaplığındaki sınıfı da kullanabilirsiniz . escapeSql(String)Kullanabileceğiniz bir yöntemi vardır :

import org.apache.commons.lang.StringEscapeUtils; … String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);


2
Başvuru için: commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/… Her neyse, bu yöntem yalnızca alıntılardan kaçar ve SQL Enjeksiyon saldırılarını engellemiyor gibi görünmektedir.
Paco Abato

11
Sadece tek tırnak kaçan çünkü bu son sürümleri kaldırıldı
Pini Cheyni

3
Bu cevap, sql enjeksiyonunu engellemediği için silinmelidir.
Javan R.

6

Eski bir sistemle uğraşıyorsanız ya da PreparedStatementçok kısa sürede e- postalara geçmek için çok fazla yeriniz varsa - yani diğer yanıtların önerdiği en iyi uygulamayı kullanmanın önünde bir engel varsa, AntiSQLFilter'ı deneyebilirsiniz .


6

Aşağıdaki koda ihtiyacınız var. Bir bakışta, bu benim oluşturduğum herhangi bir eski kod gibi görünebilir. Ancak, yaptığım şey http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.31/com/mysql/jdbc/PreparedStatement için kaynak koduna bakmaktı . java . Daha sonra, setString'in (int parameterIndex, String x) kodundan kaçtığı karakterleri bulmak için dikkatlice baktım ve bunu kendi sınıfıma göre özelleştirdim, böylece ihtiyaç duyduğunuz amaçlar için kullanılabilir. Sonuçta, Oracle'ın kaçtığı karakterlerin listesi buysa, bunu bilmek güvenlik açısından gerçekten rahatlatıcıdır. Belki de Oracle'ın bir sonraki büyük Java sürümü için buna benzer bir yöntem eklemek için bir dürtüye ihtiyacı vardır.

public class SQLInjectionEscaper {

    public static String escapeString(String x, boolean escapeDoubleQuotes) {
        StringBuilder sBuilder = new StringBuilder(x.length() * 11/10);

        int stringLength = x.length();

        for (int i = 0; i < stringLength; ++i) {
            char c = x.charAt(i);

            switch (c) {
            case 0: /* Must be escaped for 'mysql' */
                sBuilder.append('\\');
                sBuilder.append('0');

                break;

            case '\n': /* Must be escaped for logs */
                sBuilder.append('\\');
                sBuilder.append('n');

                break;

            case '\r':
                sBuilder.append('\\');
                sBuilder.append('r');

                break;

            case '\\':
                sBuilder.append('\\');
                sBuilder.append('\\');

                break;

            case '\'':
                sBuilder.append('\\');
                sBuilder.append('\'');

                break;

            case '"': /* Better safe than sorry */
                if (escapeDoubleQuotes) {
                    sBuilder.append('\\');
                }

                sBuilder.append('"');

                break;

            case '\032': /* This gives problems on Win32 */
                sBuilder.append('\\');
                sBuilder.append('Z');

                break;

            case '\u00a5':
            case '\u20a9':
                // escape characters interpreted as backslash by mysql
                // fall through

            default:
                sBuilder.append(c);
            }
        }

        return sBuilder.toString();
    }
}

2
Sanırım bu kod, yukarıdaki bağlantıdaki kaynak kodun derlenmiş versiyonu. Şimdi daha yeni olarak mysql-connector-java-xxx, case '\u00a5've case '\u20a9'ifadeleri kaldırılmış görünüyor
zhy

kodunuzla sqlmap'i denedim ve beni ilk saldırından korumadı `` Tür: boole tabanlı kör Başlık: VE boole tabanlı kör - WHERE veya HAVING yan tümcesi Yük: q =% 1 'VE 5430 = 5430 VE'% ' = ''
shareef

Çalıştığı için üzgünüm ama depolanan son oturum sonuçlarını görüntülüyordum .. yorumu gelecekteki benzer için tuttum ..
shareef

Sen kullanabilir org.ostermiller.utils.StringHelper.escapeSQL()ya com.aoindustries.sql.SQLUtility.escapeSQL().
Mohamed Ennahdi El İdrisi

1
GPLv2 lisansını, bununla karşılaşan herkes için kopyalandığı orijinal kodda not etmek önemlidir. Ben bir avukat değilim, ancak bu lisanslı kodu dahil etmenin sonuçlarının tam olarak farkında değilseniz, bu cevabı projenizde kullanmamanızı şiddetle tavsiye ederim.
Nick Spacek

0

Hazırlanmış durumları her yerde uygulayamayan eski sistem durumunda, sqlmap'i sql enjeksiyonundan korumak için bir çok çözüm araştırdıktan sonra.

Java-security-cross-site-scripting-xss-and-sql-injection konusu ÇÖZÜM OLDU

@Richard'ın çözümünü denedim ama benim durumumda işe yaramadı. filtre kullandım

Bu filtrenin amacı, isteği kendi kodlu bir MyHttpRequestWrapper içine sarmaktır.

org.springframework.web.util.HtmlUtils.htmlEscape (…) yöntemi aracılığıyla HTML kodlarına özel karakterlere (<,>, ',…) sahip HTTP parametreleri. Not: Apache Commons'ta benzer bir sınıf vardır: org.apache.commons.lang.StringEscapeUtils.escapeHtml (…) Apache Commons classe org.apache.commons.lang.StringEscapeUtils aracılığıyla SQL enjeksiyon karakterleri (', ",…). escapeSql (…)

<filter>
<filter-name>RequestWrappingFilter</filter-name>
<filter-class>com.huo.filter.RequestWrappingFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>RequestWrappingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>




package com.huo.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletReponse;
import javax.servlet.http.HttpServletRequest;

public class RequestWrappingFilter implements Filter{

    public void doFilter(ServletRequest req, ServletReponse res, FilterChain chain) throws IOException, ServletException{
        chain.doFilter(new MyHttpRequestWrapper(req), res);
    }

    public void init(FilterConfig config) throws ServletException{
    }

    public void destroy() throws ServletException{
    }
}




package com.huo.filter;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.lang.StringEscapeUtils;

public class MyHttpRequestWrapper extends HttpServletRequestWrapper{
    private Map<String, String[]> escapedParametersValuesMap = new HashMap<String, String[]>();

    public MyHttpRequestWrapper(HttpServletRequest req){
        super(req);
    }

    @Override
    public String getParameter(String name){
        String[] escapedParameterValues = escapedParametersValuesMap.get(name);
        String escapedParameterValue = null; 
        if(escapedParameterValues!=null){
            escapedParameterValue = escapedParameterValues[0];
        }else{
            String parameterValue = super.getParameter(name);

            // HTML transformation characters
            escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue);

            // SQL injection characters
            escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue);

            escapedParametersValuesMap.put(name, new String[]{escapedParameterValue});
        }//end-else

        return escapedParameterValue;
    }

    @Override
    public String[] getParameterValues(String name){
        String[] escapedParameterValues = escapedParametersValuesMap.get(name);
        if(escapedParameterValues==null){
            String[] parametersValues = super.getParameterValues(name);
            escapedParameterValue = new String[parametersValues.length];

            // 
            for(int i=0; i<parametersValues.length; i++){
                String parameterValue = parametersValues[i];
                String escapedParameterValue = parameterValue;

                // HTML transformation characters
                escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue);

                // SQL injection characters
                escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue);

                escapedParameterValues[i] = escapedParameterValue;
            }//end-for

            escapedParametersValuesMap.put(name, escapedParameterValues);
        }//end-else

        return escapedParameterValues;
    }
}

İyi java-security-cross-site-scripting-xss-and-sql-injection topicmi? Eski bir uygulama için bir çözüm bulmaya çalışıyorum.
caot

0

Gönderen: [Kaynak]

public String MysqlRealScapeString(String str){
  String data = null;
  if (str != null && str.length() > 0) {
    str = str.replace("\\", "\\\\");
    str = str.replace("'", "\\'");
    str = str.replace("\0", "\\0");
    str = str.replace("\n", "\\n");
    str = str.replace("\r", "\\r");
    str = str.replace("\"", "\\\"");
    str = str.replace("\\x1a", "\\Z");
    data = str;
  }
return data;

}


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.