applications secure by default
TRANSCRIPT
Platinum Sponsors:
#DevoxxPL
Applications secure by default
Sławomir Jasek
Pentester / security consultant.
Assessments and consultancy regarding
security of various applications - web,
mobile, embedded, ...
Since 2003 / over 400 systems and
applications
Sławomir Jasek
Code insecure by default
Blacklisting vs whitelisting
Features vs security
Access control
Beware the "silver bullets"
Fight back!
Devops
The Takeaway
Agenda
INSECURE BY DEFAULT
$url =
'https://api.twitter.com/1/statuses/public_timeline.json';
$result = file_get_contents($url);
Is there anything wrong?
The default setting does not verify hostname => Man in the Middle
$url = 'https://api.twitter.com/1/statuses/public_timeline.json';
$contextOptions = array(
'ssl' => array(
'verify_peer' => true,
'cafile' => '/etc/ssl/certs/ca-certificates.crt',
'verify_depth' => 5,
'CN_match' => 'api.twitter.com',
'disable_compression' => true,
'SNI_enabled' => true,
'ciphers' => 'ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4'
)
);
$sslContext = stream_context_create($contextOptions);
$result = file_get_contents($url, NULL, $sslContext);
The proper way
http://phpsecurity.readthedocs.org/en/latest/Transport-Layer-Security-%28HTTPS-SSL-and-TLS%29.html
Defaults to false!
file_get_contents(https language:php
file_get_contents verify_peer
Only 1 programmer in 51 uses verify_peer options.
Often to explicitly disable it ;)
The default value changed only recently in PHP 5.6.0.
But there is hope...
All the previous versions susceptible to Man-In-The-Middle attacks.
$url =
'https://api.twitter.com/1/statuses/public_timeline.json';
$req = curl_init($url);
curl_setopt($req, CURLOPT_RETURNTRANSFER, TRUE);
$result = curl_exec($req);
Curl - secure by default
// Open SSLSocket directly
SocketFactory sf = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket("mail.google.com", 443);
SSLSession s = socket.getSession();
// ... use socket ...
socket.close();
Java: SSL SocketFactory
// Open SSLSocket directly
SocketFactory sf = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket("mail.google.com", 443);
SSLSession s = socket.getSession();
// Verify that the certicate hostname is for mail.google.com
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
if (!hv.verify("mail.google.com", s)) {
throw new SSLHandshakeException("Expected mail.google.com, "
"found " + s.getPeerPrincipal());
}
// ... use socket ...
socket.close();
https://developer.android.com/training/articles/security-ssl.html#WarningsSslSocket
SSL SocketFactory
And the docs do not help
https://developer.android.com/training/articles/security-ssl.html#WarningsSslSocket
Pentester’s experience: all tested Android apps using SSLSocket were vulnerable.
Despite the bold warnings and proper example code...
URL url = new URL("https://mail.google.com");
URLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
You need to explicitly disable verification.
Pentester’s experience: only a few tested Android apps using urlConnection were deliberately broken.
HTTPS – the proper way
BLACKLISTING
Attack scenario:
http://localhost/foo/bar.action?<script>alert(1)</script>
Lack of proper output encoding.
Intruder runs hostile javascript in a victim’s browser (aka „Cross Site Scripting”).
Struts 2 – XSS vulnerability
--- struts2/trunk/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java 2008/01/24 07:37:32 614813
+++ struts2/trunk/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java 2008/01/24 07:39:45 614814
@@ -174,10 +174,14 @@
buildParametersString(params, link, "&");
}
- String result;
+ String result = link.toString();
+
+ if (result.indexOf("<script>") >= 0){
+ result = result.replaceAll("<script>", "script");
+ }
Fix - blacklisting
Attack 1:
/myAction.action?param"><sCript>alert('XSS');</sCript>=1
This is very similiar to the vulnerability in Security Bulletin S2-002; however, the implemented fix for S2-002 only checks for "<script>", not "<sCript>".
Attack 2:
/myAction.action?param"onMouseOver%3D"javascript:alert('XSS');">=1
Simply checking for <script> isn't sufficient because certain attributes can be injected to execute javascript. In attack 2, the user simply has to hover over the link with their mouse and arbitrary javascript will be executed.
https://issues.apache.org/jira/browse/WW-3410
2 years later...
-builder.append(name)
+builder.append(translateAndEncode(name))
That was 19 characters - exactly 4.78 times less than 91
characters used in first, unsuccessful „blacklist” fix.
Final fix
We found an XSS vulnerability, as usually recommended
to properly encode relevant characters in the output
context.
Retest #1: <script> does not work, but <Script> does ;)
Retest #2: <Script> nor <sCript> does not work. But onclick does.
Retest #3: onclick does not work, but onmouseover does.
Retest #4: onmouseover fixed, but onmousedown not ;)
Retest #5: ...
From the pentester's diary
+------------------------+
| XSS_PAYLOAD |
+------------------------+
| onclick= |
| ondblclick= |
| onmousedown= |
| onmousemove= |
| onmouseover= |
| onmouseout= |
| onmouseup= |
| onkeydown= |
| onkeypress= |
| onkeyup= |
| onabort= |
| onerror= |
| onload= |
| onresize= |
Finally, we broke into database via sql-injection
| onscroll= |
| onunload= |
| onblur= |
| onchange= |
| onfocus= |
| onreset= |
| onselect= |
| onsubmit= |
| onevent= |
| <script |
| script> |
| <svg |
| svg> |
| javascript: |
| <iframe | | iframe> | | <form | | form> | | <input | | ''iframe'' | | "iframe" | | document.createelement | | string.fromcharcode( | | <img/src | | submit() | | document.location. | | alert( | | <img | | <vbscript | ...
www.example.com/?file=../etc/passwd
Fix: remove ../
www.example.com/?file=....//etc/passwd
www.example.com/?file=....//etc/passwd
www.example.com/?file=../etc/passwd
Proper way: whitelist of allowed characters.
Path traversal
FEATURES VS SECURITY
Define user
# fields: [:id, :first, :last, :email, :admin]
class User < ActiveRecord::Base
End
RoR – user edit form
User edit form
user = User.find(params[:id])
user.update_attributes(params[:user])
PUT
http://app.com/users/66?user[first]=Slawomir
&user[last]=Jasek&user[email]=slawomir.jasek
@securing.pl
RoR – user edit form
PUT
http://app.com/users/66?user[first]=Slawomir
&user[last]=Jasek&user[email]=slawomir.jasek
@securing.pl&user[admin]=true
RoR – mass assignment
You were supposed to manually add whitelisted:
class User < ActiveRecord::Base
attr_accessible :first, :last, :email
end
or blacklisted parameters:
class User < ActiveRecord::Base
attr_protected :admin
end
RoR – mass assignment (2012)
GitHub PWND
http://www.h-online.com/security/news/item/GitHub-security-incident-highlights-Ruby-on-Rails-problem-1463207.html
Change default value of global config parameter turning
the mass assignment off:
# Enforce whitelist mode for mass assignment.
# This will create an empty whitelist of attributes available for mass-
assignment for all models
# in your app. As such, your models will need to explicitly whitelist or
blacklist accessible
# parameters by using an attr_accessible or attr_protected declaration.
<%= comment_if :skip_active_record %>
config.active_record.whitelist_attributes = true
Fix - evolution
config.active_record.mass_assignment_sanitizer
It will raise an
ActiveModel::MassAssignmentSecurity::Error
any time your application attempts to mass-assign something it shouldn't.
RoR 3.2: Mass assignment sanitizer
Secure by default. You must first call permit on the params hash
with the keys that are allowed for a specific action:
# Require that :user be a key in the params Hash,
# and only accept whitelisted attributes
def user_params
params.require(:user).permit(:first, :last, :email)
end
http://code.tutsplus.com/tutorials/mass-assignment-rails-and-you--net-31695
RoR 4: Strong Parameters
Before:
<%=HTMLEncoder.encode(((Person)person).getAd
dress().getStreet())%>
After (EL):
<c:out value="person.address.street"/>
Expression language – starring major frameworks
DEMO
- The security impact
Major frameworks: Spring, JBoss SEAM, Struts...
Easy to detect, automatic tools to exploit remotely into shell.
The "no man's land".
(as I have pointed out in 2012)
http://prezi.com/awm8psp-i1ok/no-mans-land
Expression Language flaws
https://play.google.com/store/apps/details?id=com.cleanmaster.security.struts2
2003: parameter names like: @System@exit(1)
"Patrick says he has fixed it" ;)
2008.06: the # can be encoded in \u0023
('\u0023' + 'session[\'user\'])(unused)=0wn3d
Released Xwork 2.0.5 – blacklisted the attack
2008.10: removed space characters and the exploit still
works ;)
('\u0023'+'session[\'user\'])(unused)=0wn3d
The fix - features vs security
2010: You can access the context and turn the settings on:
http://mydomain/MyStruts.action?('\u0023_memberAccess[\'allowStatic
MethodAccess\']')(meh)=true&(aaa)(('\u0023context[\'xwork.MethodAcc
essor.denyMethodExecution\']\u003d\u0023foo')(\u0023foo\u003dnew%20
java.lang.Boolean("false")))&(asdf)(('\u0023rt.exit(1)')(\u0023rt\u
[email protected]@getRuntime()))=1
Fix: whitelist allowed chars in parameter names
2011: User input is evaluated as an OGNL expression when there's a
conversion error.
2011: The problem concerns not only parameters, but also cookie
values
The fix – continued...
2012: fix based on whitelisting acceptable parameter names closed the vulnerability only partially. Still possible RCE with slightly modified attack syntax.
Fix: deny evaluation in parameter names.
2013: The second evaluation happens when redirect result reads it from the stack and uses the previously injected code as redirect parameter.
I won't reveal all the following episodes, I encourage you to read on:
http://struts.apache.org/docs/security-bulletins.html
There's a lot of action! Struts.action.
The fix – continued
data=%3C%3Fxml+version%3D%221.0%22%3F%3E%0A%3Cuser%3E%0A%C2%A0%C2%A0%C2%A0%C2%A0%3Cfirstname%3EJan%3C%2Ffirstname%3E%0A%C2%A0%C2%A0%C2%A0%C2%A0%3Clastname%3EKowalski%3C%2Flastname%3E%0A%3C%2Fuser%3E
HTML decoded:
<?xml version="1.0"?>
<user>
<firstname>Jan</firstname>
<lastname>Kowalski</lastname>
</user>
XML parsing
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
]>
<lolz>&lol;</lolz>
<lolz>lol</lolz>
XML parsing: DOCTYPE
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ELEMENT lolz (#PCDATA)>
]>
<lolz>&lol1;</lolz>
<lolz>lollollollollollollollollollol</lolz>
XML parsing: DOCTYPE
<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ELEMENT lolz (#PCDATA)> <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz> <user> <firstname>Jan</firstname> <lastname>Kowalski</lastname> </user>
"The Man Who Laughs"
http://en.wikipedia.org/wiki/Billion_laughs
ROTFL TO DEATH!!!
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>
As seen in Google, Facebook, Ebay and about 80%
of tested applications processing user input as XML.
It may be even worse
FB XXE
Technology Default DTD processing value
.NET 4 settings.DtdProcessing = DtdProcessing.Prohibit;
.NET 3.5 ProhibitDtd in XmlReaderSettings is true, but ProhibitDtd in XmlTextReader is false
LibXML2 (C++ ) starting with libxml2 version 2.9 (2012), XXE has been disabled by default
NSXMLDocument (iOS)
iOS5 and later: Only entities that don't require network access are loaded.
iOS4 and earlier: All external entities are loaded by default.
Xerces 2 disallow-doctype-decl=false
Xerces 1 external-general-entities=true
PHP Have to manually disable: libxml_disable_entity_loader(true);
Default DTD processing in various parsers
ACCESS CONTROL
Access control: typical scenario
Student tries to invoke administrative functions
GET /course/quiz/solutions
GET /admin/course/cancel
Student tries to access restricted data
GET /course/view/42
Student tries to alter his account rights.
POST /user/edit
roles=[student,admin]
Access control: typical attack
<security:intercept-url pattern="/user/add"
access="hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')" />
<security:intercept-url pattern="/user/view"
access="hasRole('ROLE_ADMIN')
or hasRole('ROLE_MANAGER')
or hasRole('ROLE_PRINCIPAL')
or hasRole('ROLE_TEACHER')
or hasRole('ROLE_STUDENT')" />
Approach 1: Spring Security
It works for simple apps, with unsophisticated role policies (e.g. separated admin interface).
Will not work for:
POST /user/edit HTTP/1.1
task=MODIFY_RIGHTS&id=34
Has concept of „roles”, but out of the box does not have concept of „permissions”. Spring ACL on the other hand is too complex.
Does not help a lot with access to specific instance (e.g. other user’s data)
Spring Security
Complex hard-coded checks in application code, needed to be manually loaded to every endpoint:
If (( user.isRole('ROLE_ADMIN') ||
user.isRole('ROLE_MANAGER') ||
user.isRole('ROLE_PRINCIPAL') ) ||
( user.isRole('ROLE_TEACHER') &&
user.isTeacher(course) ) ||
( user.isRole('ROLE_STUDENT') &&
(user.isStudent(course) )
...and you will probably end-up with:
One simple "if" statement, permissions separated from roles.
if ( currentUser.isPermitted("users:add") ) {
//add an user
}
int courseId=request.getInt("course")
if ( currentUser.isPermitted("courses:view:"+courseId) ) {
//show contents of course
}
Apache Shiro – permission based access control
Real story: account history – select your account
The REST API request
GET /services/history/account/85101022350445200448009906 HTTP/1.1
SA-DeviceId: 940109f08ba56a89
SA-SessionId: 826175
Accept: application/json
Host: acc
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
The REST API request
GET /services/history/account/45101022350445200448005388 HTTP/1.1
SA-DeviceId: 940109f08ba56a89
SA-SessionId: 826175
Accept: application/json
Host: acc
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
Change the acc number -> get other user’s data
John:
<select name="account">
<option value=0 >85101022350445200448009906</option>
<option value=1 >34101022350445200448009905</option>
<option value=2 >41101022350445200448009904</option>
</select>
Mary:
<select name="account">
<option value=0 >45101022350445200448005388</option>
<option value=1 >31101022350445200448005390</option>
</select>
The better way?
"Local" ID mapped server-side into real values
The better way?
SA-DeviceId: d4c79a0fd994b1f3
SA-SessionId: 850073
GET /services/history/account/1 HTTP/1.1
SA-DeviceId: 940109f08ba56a89
SA-SessionId: 826175
Accept: application/json
Host: acc
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
It is(?) by design not possible to attack other user's data.
Would be great if not...
John: GET /services/history/account/2 HTTP/1.1
SA-DeviceId: 940109f08ba56a89
SA-SessionId: 826175
Accept: application/json
Host: acc
Connection: Keep-Alive
API just works as a proxy to backend system. Default session mechanism from backend – just incrementing IDs ;)
Mary: GET /services/history/account/2 HTTP/1.1
SA-DeviceId: d4c79a0fd994b1f3
SA-SessionId: 826179
Accept: application/json
Host: acc
Connection: Keep-Alive Trivial to guess other user's sessionId
Automatically encrypts paths with individual, session-based and unique key:
www.app.com/admin/user/edit
www.app.com/?x=XSn4MUyv6Uke22d9vEuGgR8yZa5-TkeuxlXeeVypcehJ8HQgNbj(...)
Apache Wicket
The browser works on rendered GUI elements, POSTs to the server event-driven actions (e.g. mouse clicks in certain location).
It is by design not possible to invoke actions that are not in GUI.
POST /dashboard/UIDL/?v-uiId=0 HTTP/1.1
Host: demo.vaadin.com
{"csrfToken":"981b1cd6-66df-481c-9d6e-6c293eb70ea3","rpc":[["283","v","v",["positionx",["i","440"]]],["283","v","v",["positiony",["i","139"]]],["309","com.vaadin.shared.ui.button.ButtonServerRpc","click",[{"altKey":false,"relativeX":"42","relativeY":"23","ctrlKey":false,"button":"LEFT","shiftKey":false,"clientX":"1109","clientY":"598","metaKey":false,"type":"1"}]]],"syncId":14}
Vaadin
BEWARE
There's no silver bullet!
Hadi Hariri keynote on Monday
Automagic output encoding by framework
A framework automatically encodes special chars into
HTML:
<bean:write name="transferFormId" property="trn_recipient"/>
ATTACK: trn_recipient="<script>alert('xss')</script>
<input type="text" name="trn_recipient"
value=""<script>alert('xss')</script>"
Beware the security mechanism use cases
But unfortunatelly that does not help when you put a value from end-user directly into javascript context:
<script> var split='<bean:write name="transferFormId" property="trn_recipient">'; splitRecipient(split); </script>
ATTACK: trn_recipient=';alert('xss');-- <script> var split='';alert('xss');--
Encode special chars properly in context!
• HTML element
• HTML atribute
• JavaScript
• JSON
• CSS / style
• URL
Encode properly
Prepared statement / call
String sql = "select * from users where
firstname=? and lastname=?";
query = conn.prepareStatement(sql);
firstname = request.getParameter("first");
lastname = request.getParameter("last");
query.setString(1, firstname);
query.setString(2, lastname);
result = query.executeQuery();
String sql = "{call
USERS.search(" + "?" + ", ?)}";
call = conn.prepareCall(sql);
firstname = request.getParameter("first");
lastname = request.getParameter("last");
call.setString(1, firstname);
call.setString(2, lastname);
call.execute();
prepared statement prepared call
Called stored procedure
PROCEDURE search(
p_firstname IN T_STRING,
p_lastname IN T_STRING,
) IS
(...)
v_sql_select := ' SELECT distinct a.USER_ID';
v_sql_from := ' FROM APP_WEB.USERS a ';
v_sql_where := ' WHERE a.USER_ID is not null ';
IF p_firstname is not null THEN
v_sql_where := v_sql_where || ' and lower(trim(a.FIRSTNAME)) =
lower(trim(' || P_FIRSTNAME || ')) ';
END IF;
SQL injection inside stored procedure
PROCEDURE search(
p_firstname IN T_STRING,
p_lastname IN T_STRING,
) IS
(...)
v_sql_select := ' SELECT distinct a.USER_ID';
v_sql_from := ' FROM APP_WEB.USERS a ';
v_sql_where := ' WHERE a.USER_ID is not null ';
IF p_firstname is not null THEN
v_sql_where := v_sql_where || ' and lower(trim(a.FIRSTNAME)) =
lower(trim('|| 'adam')) union select version,'x' from v$instance-- || ')) ';
END IF;
NoSQL. There is no sql injection.
There is nosql injection!
Instead of
' OR 1=1 --
try
a'; return 1=1; var dummy='a
NoSQL
Change SMS authorization phone number in internet banking application
Application logic flaws
What could possibly go wrong?
Change SMS authorization phone number in internet banking application
The scenario missed in functional tests
Change SMS authorization phone number in internet banking application
The evil wins
The application did verify only the SMS code from the new phone. And intruder with access to user session can take over the authorization.
FIGHT BACK!
- Intrusion detection and prevention
• Input validation server-side when client-side validation exists
• Non-user editable parameters/values (hidden fields, checkboxes, radio buttons, select lists)
• Forced browsing to common attack entry points or honeypot URL (e.g. in robots.txt)
• Obvious sqli, xss inj attacks
• Workflow sequence abuse
Intrusion detection – level basic
http://www.slideshare.net/JimManico/top-ten-defenses-v10 slide 60
One of the most annoying experiences during test was
automatic logout on every test-case in application that required manual interaction to authenticate.
Active intrusion prevention
OWASP AppSensor
Conceptual framework and methodology to implement intrusion detection and
automated response into applications.
https://www.owasp.org/index.php/OWASP_AppSensor_Project
OWASP mod_security core ruleset
An easily "pluggable" set of generic attack detection rules that
provide a base level of protection for any web application.
https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project
Basic tools
DEVOPS
- Default infrastructure
Default error handling
Reveals information on used components, helps to attack known vulnerabilities
Trading application – binary protocol
Trading application – binary protocol
Trading application – binary protocol
And what if we...
And how about...
<soapenv:Body> <registerUserResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<registerUserReturn xsi:type="xsd:string">
<error code="266" >Incorrect login</error>
</registerUserReturn></registerUserResponse></soapenv:Body>
• Incorrect password
• Incorrect first name
• Group with name null doesn't exist
• Group with name admin doesn't exist
• Group with name Administrator doesn't exist
• And how about „root”?
RegisterUser
Game Over
<soapenv:Body>
<registerUserResponse
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/
encoding/">
<registerUserReturn xsi:type="xsd:string">
User was registered sucessfully with id=5392745
Access to system with administartor rights. Possible to manage accounts of all other users.
Architecture
Default HTTP error response
Reveals used version
Known vulnerabilities based on version
Apache Tomcat < 6.0.20
<role rolename="tomcat"/>
<role rolename="role1"/>
<user username="tomcat" password="tomcat" roles="tomcat"/>
<user username="both" password="tomcat" roles="tomcat,role1"/>
<user username="role1" password="tomcat" roles="role1"/>
tomcat-users.xml
Apache Tomcat Manager
Tomcat worm using weak passwords
http://www.symantec.com/connect/blogs/all-your-tomcat-are-belong-bad-guys
tryLogins = { "admin:admin", "tomcat:tomcat", "admin:", "tomcat:", "root:root", "manager:manager", "tomcat:admin", "admin:password"};
OWASP dependency check / dependency track
Automatically checks for known vulnerabilities in used components
https://www.owasp.org/index.php/OWASP_Dependency_Track
THE TAKEAWAY
• Use secure design architecture
• Least privilege principle (default: deny)
• Code that enforces good practices
• Leverage existing security mechanisms, but be aware of their shortcomings and secure use scenario.
• Secure configuration
• Keep the components up to date
• Change default credentials
• Harden the configuration
• Leverage additional layers of protection (IDS, WAF)
Key takeaways
Our presentations (including this one), resources
www.securing.pl/en/resources/
Free security consultancy service:
www.securing.pl/konsultacje
See also
And for the Happy(?)-End – the pentester’s view
Features at low cost compromising on security is just obscene ;) Let’s do it better!
Thank you,
looking forward to contact!
MORE THAN SECURITY TESTING
Free security consultancy service:
www.securing.pl/konsultacje