Запобігання XSS у веб-додатку JSP / Servlet


Відповіді:


113

XSS можна запобігти в JSP, використовуючи тег JSTL <c:out> або fn:escapeXml()функцію EL при (повторному) відображенні контрольованого користувачем вводу . Це включає параметри запиту, заголовки, файли cookie, URL-адресу, тіло тощо. Все, що ви витягуєте з об’єкта запиту. Також керований користувачем вхід з попередніх запитів, який зберігається в базі даних, повинен бути захищений під час повторного відображення.

Наприклад:

<p><c:out value="${bean.userControlledValue}"></p>
<p><input name="foo" value="${fn:escapeXml(param.foo)}"></p>

Це уникне символів , які можуть malform обробленої HTML , такі як <, >, ", 'і &в сутностей HTML / XML , такі як &lt;, &gt;, &quot;, &apos;і &amp;.

Зверніть увагу, що вам не потрібно уникати їх у коді Java (Servlet), оскільки вони там нешкідливі. Деякі з них можуть вибрати , щоб уникнути їх під час запиту обробки (як це робиться в сервлет або фільтр) замість відповіді обробки (як це робиться в JSP), але в цьому випадку ви ризикуєте , що дані надмірно отримати подвійним екрануванням (наприклад , &стає &amp;amp;замість &amp;і зрештою споживач побачить&amp;дані, що зберігаються в БД, стають непереносимими (наприклад, під час експортування даних у JSON, CSV, XLS, PDF тощо, що взагалі не вимагає HTML-екранування). Ви також втратите соціальний контроль, бо вже не знаєте, що насправді заповнив користувач. Ви як адміністратор сайту дуже хотіли б знати, які користувачі / IP-адреси намагаються виконати XSS, щоб ви могли легко відстежувати їх і відповідно вжити заходів. Втечу під час обробки запитів слід використовувати і використовувати лише як найсвіжіший варіант, коли вам дійсно потрібно якомога коротше виправити аварію поїзда погано розробленої застарілої веб-програми. Тим не менш, вам слід остаточно переписати свої файли JSP, щоб стати безпечними для XSS.

Якщо ви хочете , щоб знову відобразити призначений для користувача керований введення як HTML , в якому ви хотіли б, щоб тільки певну підмножину HTML - теги , як <b>, <i>, <u>і т.д., то вам необхідно дезінфікувати вхід з допомогою білого списку. Для цього можна використовувати синтаксичний аналізатор HTML, такий як Jsoup . Але набагато краще ввести зручну для людей мову розмітки, таку як Markdown (також використовується тут, у Stack Overflow). Тоді ви можете використовувати для цього парсер Markdown, такий як CommonMark . Він також має вбудовані можливості дезінфекції HTML. Див. Також Markdown або HTML .

Єдине занепокоєння на стороні сервера щодо баз даних - це запобігання ін'єкції SQL . Потрібно переконатись, що ви ніколи не об’єднували рядки, керовані користувачем, безпосередньо в запиті SQL або JPQL і використовували параметризовані запити до кінця. У термінах JDBC це означає, що вам слід використовувати PreparedStatementзамість Statement. З точки зору JPA, використовуйте Query.


Альтернативою може бути перехід із JSP / Servlet на MVC-платформу JSF Java EE . Він вбудував запобігання XSS (і CSRF!) У всьому місці. Див. Також запобігання атакам CSRF, XSS та SQL Injection у JSF .


1
Те, що ви використовуєте Hibernate, не означає, що ви в безпеці від введення SQL. Дивіться, наприклад, blog.harpoontech.com/2008/10/… .
MatrixFrog

@chad: це неправда. Це лише той випадок, коли ви зв’язуєте рядки керованим користувачем введенням прямо в запиті SQL / HQL / JPQL так, "SELECT ... WHERE SOMEVAL = " + somevalзамість того, щоб використовувати параметризовані запити, як ви показали. Ніхто ORM не може захистити від такого роду помилок розробника.
BalusC

5
Я думаю, вам теж потрібно перевірити на сервері. Всю перевірку можна обійти, змінивши параметри HTTP. Іноді дані, які ви зберігаєте, можуть бути використані іншими програмами в корпоративній програмі. Іноді у вас немає доступу до подань інших програм, тому вам потрібно пропрацювати введення, перш ніж зберігатись у базі даних.
Guido Celada,

1
@Guido: ти не розумієш проблеми.
BalusC

2
@peater: Так, коли ви розміщуєте ненадійні дані всередині коду JS, вам потрібно JS-кодування замість HTML-кодування. І, розміщуючи ненадійні дані всередині коду CSS, вам потрібно кодувати CSS замість кодування HTML. І, розміщуючи ненадійні дані всередині URL-адрес, вам потрібно кодувати URL замість HTML-кодування. Кодування HTML слід використовувати лише для розміщення ненадійних даних всередині коду HTML.
BalusC

12

Як запобігти-xss запитували кілька разів. Ви знайдете багато інформації в StackOverflow. Крім того, на веб-сайті OWASP є шпаргалка щодо запобігання XSS, яку вам слід пройти.

Щодо бібліотек, які слід використовувати, бібліотека ESAPI OWASP має смак Java. Ви повинні спробувати це. Крім того, кожен фреймворк, який ви використовуєте, має певний захист від XSS. Знову ж таки, веб-сайт OWASP містить інформацію про найпопулярніші фреймворки, тому я б рекомендував переглянути їхній сайт.


Шпаргалки OWASP перенесено на GitHub. Ось посилання на шпаргалку
peater

12

Мені пощастило з OWASP Anti-Samy та радником AspectJ на всіх моїх контролерах Spring, що блокує XSS від потрапляння.

public class UserInputSanitizer {

    private static Policy policy;
    private static AntiSamy antiSamy;

    private static AntiSamy getAntiSamy() throws PolicyException  {
        if (antiSamy == null) {
            policy = getPolicy("evocatus-default");
            antiSamy = new AntiSamy();
        }
        return antiSamy;

    }

    public static String sanitize(String input) {
        CleanResults cr;
        try {
            cr = getAntiSamy().scan(input, policy);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return cr.getCleanHTML();
    }

    private static Policy getPolicy(String name) throws PolicyException {
        Policy policy = 
            Policy.getInstance(Policy.class.getResourceAsStream("/META-INF/antisamy/" + name + ".xml"));
        return policy;
    }

}

Ви можете отримати радника AspectJ з цього посту stackoverflow

Я думаю, що це кращий підхід, ніж c: out, особливо якщо ви робите багато javascript.


Звичайною практикою є уникнення HTML-даних, керованих користувачем, під час повторного відображення, а не під час обробки поданих даних у сервлеті або під час зберігання в БД. Якщо ви уникнете HTML-коду під час обробки поданих даних та / або зберігання також у БД, то це все розподілиться по коду бізнесу та / або в базі даних. Це лише проблеми з технічним обслуговуванням, і ви ризикуєте подвоїтись чи більше, якщо зробите це в різних місцях. Бізнес-код і БД, в свою чергу, не є чутливими до XSS. Тільки вид є. Тоді вам слід уникнути цього лише там, де видно.
Шубхем Махешварі,

1
Так і ні. Хоча загальна практика полягає в тому, щоб уникнути демонстрації, існує багато причин, через які ви можете захотіти продезінфікувати під час написання. Є деякі випадки, коли ви хочете, щоб ваші користувачі ввели підмножину HTML, і хоча ви можете дезінфікувати на дисплеї, це насправді досить повільно і навіть бентежить користувачів. По-друге, якщо ви ділитесь даними зі сторонніми службами, такими як зовнішні API, ці служби можуть, а можуть і не зробити належну санітарну обробку.
Адам Гент,

як ми з вами згадали, "звичайною практикою" є втеча на показ. Те, що ви згадали у своєму вищезазначеному коментарі, є більш конкретними варіантами використання, і, отже, вимагає певних рішень.
Shubham Maheshwari

Так, я, мабуть, мав би пояснити свій варіант використання. Я працюю над речами управління вмістом (редагування HTML).
Адам Гент,

8

Управління XSS вимагає декількох перевірок, даних з боку клієнта.

  1. Вхідні перевірки (перевірка форми) на стороні сервера. Існує кілька способів зробити це. Ви можете спробувати перевірку компонента JSR 303 ( сплячий валідатор ) або ESAPI Input Validation framework . Хоча я ще (ще) не пробував, є анотація, яка перевіряє безпечний html (@SafeHtml) . Ви могли б фактично використовувати валідатор Hibernate з Spring MVC для перевірки компонентів -> Посилання
  2. Вихідні запити URL-адреси - для всіх ваших запитів HTTP використовуйте якийсь фільтр XSS. Я використовував наступне для нашого веб-додатку, і він дбає про очищення запиту URL-адреси HTTP - http://www.servletsuite.com/servlets/xssflt.htm
  3. Вхідні дані / html повертаються клієнту (див. Вище пояснення @BalusC).

3

Я б запропонував регулярно тестувати на вразливості за допомогою автоматизованого інструменту та виправляти все, що він знаходить. Набагато простіше запропонувати бібліотеку, яка допоможе вирішити певну вразливість, ніж для всіх атак XSS загалом.

Skipfish - це інструмент з відкритим кодом від Google, який я досліджував: він знаходить досить багато речей і, здається, вартий використання.


Профілактика є кращою, ніж діагностика (наприклад, пропуск риби) з подальшим швидким виправленням.
Sripathi Krishnan

2
Я не погоджуюсь. Профілактика без діагностики - це просто догма. Запустіть діагностику як частину циклу ДІ, щоб уникнути проблеми "швидкого виправлення".
Шон Рейлі,

3

Існує не просто, нестандартне рішення проти XSS. API OWASP ESAPI має певну підтримку для виходу, що є дуже корисним, і вони мають бібліотеки тегів.

Мій підхід полягав у тому, щоб розширити теги stuts 2 наступними способами.

  1. Змінити тег s: property, щоб він міг приймати додаткові атрибути, вказуючи, який тип екранування необхідний (escapeHtmlAttribute = "true" тощо). Це передбачає створення нових класів Property і PropertyTag. Клас властивості використовує API OWASP ESAPI для екранування.
  2. Змініть шаблони безкоштовних маркерів, щоб використовувати нову версію властивості s: і встановіть екранування.

Якщо ви не хочете змінювати класи на кроці 1, іншим підходом буде імпорт тегів ESAPI в шаблони безкоштовних маркерів та втеча за необхідності. Тоді, якщо вам потрібно використовувати як: тег властивості у своєму JSP, оберніть його та тегом ESAPI.

Я написав тут більш детальне пояснення.

http://www.nutshellsoftware.org/software/securing-struts-2-using-esapi-part-1-securing-outputs/

Я погоджуюсь, що вхідні дані не є ідеальними.


2

Я вважаю, що вам слід уникати використання сторінок JSP / ASP / PHP / тощо. Натомість вихід в API, подібний до SAX (призначений лише для викликів, а не для обробки). Таким чином існує єдиний шар, який повинен створити добре сформований результат.


2

Якщо ви хочете автоматично уникнути всіх змінних JSP без необхідності явно обертати кожну змінну, ви можете використати вирішувач EL, як описано тут із повним джерелом та прикладом (JSP 2.0 або новіший) , і докладніше обговорено тут :

Наприклад, використовуючи згаданий вище розпізнавач EL, ваш код JSP залишиться таким, але кожна змінна буде автоматично захищена від роздільника

...
<c:forEach items="${orders}" var="item">
  <p>${item.name}</p>
  <p>${item.price}</p>
  <p>${item.description}</p>
</c:forEach>
...

Якщо ви хочете примусово втекти за замовчуванням навесні, ви можете розглянути і це, але це не уникне виразів EL, просто виведіть тег, я думаю:

http://forum.springsource.org/showthread.php?61418-Spring-cross-site-scripting&p=205646#post205646

Примітка: Інший підхід до евакуації EL, який використовує перетворення XSL для попередньої обробки файлів JSP, можна знайти тут:

http://therning.org/niklas/2007/09/preprocessing-jsp-files-to-automatically-escape-el-expressions/


Привіт, Бред, я шукаю вищезазначений випадок. Не могли б ви допомогти пояснити, як ви можете запобігти xss у випадку вищезазначеного сценарію (для кожного з них)
RockingDev

Єдиний, що я справді пам’ятаю зараз, - це використання розширювача EL - це те, що ми в підсумку використали в нашій компанії. В основному він автоматично уникає всього, і якщо ви дійсно не хочете, щоб щось уникло, ви можете обернути це, <enhance:out escapeXml="false">як описано в статті.
Бред Паркс

0

Якщо ви хочете переконатись, що ваш $оператор не страждає від злому XSS, ви можете застосувати ServletContextListenerта зробити там деякі перевірки.

Повне рішення за адресою: http://pukkaone.github.io/2011/01/03/jsp-cross-site-scripting-elresolver.html

@WebListener
public class EscapeXmlELResolverListener implements ServletContextListener {
    private static final Logger LOG = LoggerFactory.getLogger(EscapeXmlELResolverListener.class);


    @Override
    public void contextInitialized(ServletContextEvent event) {
        LOG.info("EscapeXmlELResolverListener initialized ...");        
        JspFactory.getDefaultFactory()
                .getJspApplicationContext(event.getServletContext())
                .addELResolver(new EscapeXmlELResolver());

    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        LOG.info("EscapeXmlELResolverListener destroyed");
    }


    /**
     * {@link ELResolver} which escapes XML in String values.
     */
    public class EscapeXmlELResolver extends ELResolver {

        private ThreadLocal<Boolean> excludeMe = new ThreadLocal<Boolean>() {
            @Override
            protected Boolean initialValue() {
                return Boolean.FALSE;
            }
        };

        @Override
        public Object getValue(ELContext context, Object base, Object property) {

            try {
                    if (excludeMe.get()) {
                        return null;
                    }

                    // This resolver is in the original resolver chain. To prevent
                    // infinite recursion, set a flag to prevent this resolver from
                    // invoking the original resolver chain again when its turn in the
                    // chain comes around.
                    excludeMe.set(Boolean.TRUE);
                    Object value = context.getELResolver().getValue(
                            context, base, property);

                    if (value instanceof String) {
                        value = StringEscapeUtils.escapeHtml4((String) value);
                    }
                    return value;
            } finally {
                excludeMe.remove();
            }
        }

        @Override
        public Class<?> getCommonPropertyType(ELContext context, Object base) {
            return null;
        }

        @Override
        public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base){
            return null;
        }

        @Override
        public Class<?> getType(ELContext context, Object base, Object property) {
            return null;
        }

        @Override
        public boolean isReadOnly(ELContext context, Object base, Object property) {
            return true;
        }

        @Override
        public void setValue(ELContext context, Object base, Object property, Object value){
            throw new UnsupportedOperationException();
        }

    }

}

Ще раз: Це лише охороняє $. Будь ласка, перегляньте також інші відповіді.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.