大聪明教你学Java 没有绝对安全的系统(1)

前言

不知道各位小伙伴有没有看过巴伦·博·欧达尔执导的悬疑推理片——《我是谁:没有绝对安全的系统》,电影中的主人公和他的朋友们一起组建了黑客组织
CLAY(Clawn Laghing At You
意思是小丑的嘲笑),并且先后入侵了国际安全系统、国际金融系统、国际金融评估系统、德国安全局、德国情报局。身为一名程序员的我在看到电影中的主人公利用系统漏洞进行入侵时,不由自主的就想到了自己参与开发的系统软件肯定或多或少也存在漏洞(估计这些软件在人家手里撑不过三分钟吧😂)。我们无法保证系统一点漏洞都没有,更无法保证系统是绝对安全的,我们能做的只有尽可能的去发现漏洞并修复漏洞,让我们的系统尽可能的安全一些。那么今天就跟大家总结一下常见的漏洞以及对应的解决方案💪。

P.S.
虽然这只是一部电影,其中的剧情会有所虚构,但是当我看完电影以后还是说出了这句话:这群人太🐂批了(已经无法用其他言语来形容了)!这确实是一部值得二刷三刷的电影

常见安全漏洞及解决方案

SQL 注入
什么是 SQL 注入

SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的 BUG
来实现的攻击,而是针对程序员编写时的一时大意,通过SQL语句来进行入侵,实现无账号登录,甚至篡改数据库。

咱们举个例子:比如现在有一个登录的功能,我们在代码中对用户提交进来的参数(账号、密码等)进行拼接,最终拼接成完整的 SQL 语句对用户身份进行校验👇

String sql = “select * from user where username=’ “+userName+” ’ and
password=’ “+password+” '”;

如果这时候有人输入了非法参数进行提交,就有可能让上面的语句变成这样👇

select * from user where username=‘xxx’ or 1 = 1 - - and password=’ ’

我们来分析一下这条SQL语句:where 条件后面的 username=‘xxx’ or 1 = 1
的含义就是“用户名等于xxx或者1=1”,那么此时这个条件就变成了永真的状态;咱们在往后看,and 关键字前面多了两个小横线,这个横线肯定都不陌生吧?在
SQL 语句中两条小横线代表了注释,这样一来就使得 and 后面对密码的校验失效了,也就是说上面的语句是一条一定可以执行成功的语句,这样也就达成了 SQL
注入的目的。
这还是一个比较温柔的 SQL 注入,那如果 SQL 语句变成了这样呢?👇

select * from user where username=‘xxx’ ; DROP DATABASE (DB Name); - - and
password=’ ’

如果真的将这句 SQL 成功的在数据库内执行了,那后果就不堪设想了…

SQL 注入的解决方案

但凡有 SQL 注入漏洞的程序,都是因为程序要接受来自客户端用户输入的变量或者 URL
传递的参数,并且这个变量或参数是组成SQL语句的一部分,对于用户输入的内容或传递的参数,我们应该要时刻保持警惕,要严格遵循外部数据不可信任的原则
,纵观 Web
安全领域的各种攻击方式,大多数都是因为开发者违反了这个原则而导致的,所以自然能想到的,就是从参数的检测、过滤、验证下手,确保参数是开发者所预想的安全参数。

检查变量数据类型和格式 :如果你的 SQL 语句是类似于 where id={$id} 的形式,数据库里所有的 id 都是数字,那么就应该在
SQL 语句被执行前,检查接受到的 id 参数的类型是不是 int
类型;如果是接受手机号码参数,那就应该检查并严格确保参数一定是手机号码的格式,其他类型的参数也是一个道理。总之就是一句话:只要是有固定格式的参数(数字、手机号码、邮箱等等),在
SQL 语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样就可以在很大程度上防止 SQL 注入攻击。
我们一开始举了一个校验用户名和密码的例子,如果我们在产品的设计阶段有一个用户名的规则,比如规定了用户名的长度必须是5-15个字符,而且用户名只能由大小写字母、数字以及一些安全的符号组成,不包含特殊字符。那么此时我们就应该写一个check_username(检查用户名)的方法,用该方法对接收到的用户名参数进行统一检查。但是在文章发布系统,评论系统等必须要允许用户提交任意字符串的场景下,上述的校验方式就不合适了,此时就需要采用过滤器等其他方案了。

P.S. 关于固定格式参数的校验可以使用 Hutool 工具类库,让你的代码变得“甜甜的”~ 没接触过 Hutool 的小伙伴可以参考大聪明教你学Java | Hutool - A set of tools that keep Java sweet

过滤特殊符号 :这句话就很好理解了,对于一些没有固定格式的参数,我们就需要对其进行特殊字符的过滤,禁止用户传递不安全的特殊符号。

使用预编译语句 :所谓预编译语句就是将 SQL 语句中的变量值用占位符替代,可以说是将 SQL
语句模板化或者说参数化,一般称这类语句叫Prepared Statements或者 Parameterized
Statements。绑定变量使用预编译语句是预防 SQL 注入的最佳方式,使用预编译的 SQ
L语句的语义不会发生改变,在SQL语句中,变量用问号?表示,这时候就算黑客本事再大,即便传递进来了 SQL
注释符号(也就是上面提到的两条横线),那也无法改变整个 SQL 语句的结构。

如果使用了Mybatis,那么在编写映射语句时,尽量采用 #{xxx} 这样的格式 :在MyBatis中, $
{xxx}这样格式的参数会直接参与 SQL 编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用 ${xxx}
这样的参数格式。所以,这样的参数需要我们在代码中手工进行处理来防止出现 SQL 注入。

跨站脚本漏洞
什么是跨站脚本漏洞

跨站脚本漏洞(Cross-site scripting,通常简称为XSS)发生在客户端,可被用于进行窃取隐私、钓鱼欺骗、窃取密码、传播恶意代码等攻击。XSS
攻击使用到的技术主要为 HTML 和 Javascript,也包括 VBScript 和 ActionScript 等。XSS 攻击对 WEB
服务器虽无直接危害,但是它借助网站进行传播,使网站的使用用户受到攻击,导致网站用户帐号被窃取,从而对网站也产生了较严重的危害。
说白了 XSS 攻击就是往 Web 页面里插入恶意脚本代码,当用户浏览该页之时,嵌入其中的恶意脚本代码会被执行,从而达到恶意攻击用户的目的。XSS
攻击一共分为以下三类👇

  1. 反射型XSS<非持久化>:攻击者事先制作好攻击链接, 需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面,或者出现在垃圾短信。(未知链接不要随意点击哦,很危险的🈲)
  2. 存储型XSS<持久化> :这种 XSS 攻击是将代码存储到了服务器中的,比如黑客在修改个人信息或发表文章等地方上传了恶意代码,此时如果没有过滤或过滤不严,那么这些代码将储存到服务器中,每当有用户访问该页面的时候都会触发代码执行,这种 XSS 攻击就非常危险了,容易造成蠕虫或者是导致大量 Cookie 失窃。
  3. DOM型XSS:是一种发生在客户端 DOM(Document Object Model 文档对象模型)中的跨站漏洞,很大原因是因为客户端脚本处理逻辑导致的安全问题。
跨站脚本漏洞的解决方案

与SQL注入防护的建议一样,我们依然要严格遵循外部数据不可信任的原则 ,必须对所有输入中的script、iframe
等字样进行严格的检查。这里的输入不仅仅是用户可以直接交互的输入接口,也包括 HTTP 请求中的 Cookie 中的变量,HTTP
请求头部中的变量等。这里就推荐使用过滤器来进行参数校验了,毕竟需要检查校验的地方这么多,总不能一个一个的写吧~ 直接上代码👇

import java.util.\*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public final class HTMLFilter {
 
    /\*\* regex flag union representing /si modifiers in php \*\*/
    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
    private static final Pattern P_COMMENTS = Pattern.compile("<!--(.\*?)-->", Pattern.DOTALL);
    private static final Pattern P_COMMENT = Pattern.compile("^!--(.\*)--$", REGEX_FLAGS_SI);
    private static final Pattern P_TAGS = Pattern.compile("<(.\*?)>", Pattern.DOTALL);
    private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
    private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.\*?)(/?)$", REGEX_FLAGS_SI);
    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.\*?)\\2", REGEX_FLAGS_SI);
    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
    private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
    private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
    private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
    private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
    private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]\*)(?=(;|&|$))");
    private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
    private static final Pattern P_END_ARROW = Pattern.compile("^>");
    private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]\*?)(?=<|$)");
    private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]\*?)(?=>)");
    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]\*?)(?=<|$)");
    private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]\*?)(?=>)");
    private static final Pattern P_AMP = Pattern.compile("&");
    private static final Pattern P_QUOTE = Pattern.compile("<");
    private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
    private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
    private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
 
    // @xxx could grow large... maybe use sesat's ReferenceMap
    private static final ConcurrentMap<String,Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<String, Pattern>();
    private static final ConcurrentMap<String,Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<String, Pattern>();
 
    /\*\* set of allowed html elements, along with allowed attributes for each element \*\*/
    private final Map<String, List<String>> vAllowed;
    /\*\* counts of open tags for each (allowable) html element \*\*/
    private final Map<String, Integer> vTagCounts = new HashMap<String, Integer>();
 
    /\*\* html elements which must always be self-closing (e.g. "<img />") \*\*/
    private final String[] vSelfClosingTags;
    /\*\* html elements which must always have separate opening and closing tags (e.g. "<b></b>") \*\*/
    private final String[] vNeedClosingTags;
    /\*\* set of disallowed html elements \*\*/
    private final String[] vDisallowed;
    /\*\* attributes which should be checked for valid protocols \*\*/
    private final String[] vProtocolAtts;
    /\*\* allowed protocols \*\*/
    private final String[] vAllowedProtocols;
    /\*\* tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") \*\*/
    private final String[] vRemoveBlanks;
    /\*\* entities allowed within html markup \*\*/
    private final String[] vAllowedEntities;
    /\*\* flag determining whether comments are allowed in input String. \*/
    private final boolean stripComment;
    private final boolean encodeQuotes;
    private boolean vDebug = false;
    /\*\*
 \* flag determining whether to try to make tags when presented with "unbalanced"
 \* angle brackets (e.g. "<b text </b>" becomes "<b> text </b>"). If set to false,
 \* unbalanced angle brackets will be html escaped.
 \*/
    private final boolean alwaysMakeTags;
 
    /\*\* Default constructor.
 \*
 \*/
    public HTMLFilter() {
        vAllowed = new HashMap<>();
 
        final ArrayList<String> a_atts = new ArrayList<String>();
        a_atts.add("href");
        a_atts.add("target");
        vAllowed.put("a", a_atts);
 
        final ArrayList<String> img_atts = new ArrayList<String>();
        img_atts.add("src");
        img_atts.add("width");
        img_atts.add("height");
        img_atts.add("alt");
        vAllowed.put("img", img_atts);
 
        final ArrayList<String> no_atts = new ArrayList<String>();
        vAllowed.put("b", no_atts);
        vAllowed.put("strong", no_atts);
        vAllowed.put("i", no_atts);
        vAllowed.put("em", no_atts);
 
        vSelfClosingTags = new String[]{"img"};
        vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
        vDisallowed = new String[]{};
        vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
        vProtocolAtts = new String[]{"src", "href"};
        vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
        vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
        stripComment = true;
        encodeQuotes = true;
        alwaysMakeTags = true;
    }
 
    /\*\* Set debug flag to true. Otherwise use default settings. See the default constructor.
 \*
 \* @param debug turn debug on with a true argument
 \*/
    public HTMLFilter(final boolean debug) {
        this();
        vDebug = debug;
 
    }
 
    /\*\* Map-parameter configurable constructor.
 \*
 \* @param conf map containing configuration. keys match field names.
 \*/
    public HTMLFilter(final Map<String,Object> conf) {
 
        assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
        assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
        assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
        assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
        assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
        assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
        assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
        assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
 
        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
        vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
        vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
        vDisallowed = (String[]) conf.get("vDisallowed");
        vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
        vProtocolAtts = (String[]) conf.get("vProtocolAtts");
        vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
        vAllowedEntities = (String[]) conf.get("vAllowedEntities");
        stripComment =  conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
        encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
        alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
    }
 
    private void reset() {
        vTagCounts.clear();
    }
 
    private void debug(final String msg) {
        if (vDebug) {
            Logger.getAnonymousLogger().info(msg);
        }
    }
 
    //---------------------------------------------------------------
    // my versions of some PHP library functions
    public static String chr(final int decimal) {
        return String.valueOf((char) decimal);
    }
 
    public static String htmlSpecialChars(final String s) {
        String result = s;
        result = regexReplace(P_AMP, "&amp;", result);
        result = regexReplace(P_QUOTE, "&quot;", result);
        result = regexReplace(P_LEFT_ARROW, "&lt;", result);
        result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
        return result;
    }
 
    //---------------------------------------------------------------
    /\*\*
 \* given a user submitted input String, filter out any invalid or restricted
 \* html.
 \*
 \* @param input text (i.e. submitted by a user) than may contain html
 \* @return "clean" version of input, with only valid, whitelisted html elements allowed
 \*/
    public String filter(final String input) {
        reset();
        String s = input;
 
        debug("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*");
        debug(" INPUT: " + input);
 
        s = escapeComments(s);
        debug(" escapeComments: " + s);
 
        s = balanceHTML(s);
        debug(" balanceHTML: " + s);
 
        s = checkTags(s);
        debug(" checkTags: " + s);
 
        s = processRemoveBlanks(s);
        debug("processRemoveBlanks: " + s);
 
        s = validateEntities(s);
        debug(" validateEntites: " + s);
 
        debug("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\n\n");
        return s;
    }
 
    public boolean isAlwaysMakeTags(){
        return alwaysMakeTags;
    }
 
    public boolean isStripComments(){
        return stripComment;
    }
 
    private String escapeComments(final String s) {
        final Matcher m = P_COMMENTS.matcher(s);
        final StringBuffer buf = new StringBuffer();
        if (m.find()) {
            final String match = m.group(1); //(.\*?)
            m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
        }
        m.appendTail(buf);
 
        return buf.toString();
    }
 
    private String balanceHTML(String s) {
        if (alwaysMakeTags) {
            //
            // try and form html
            //
            s = regexReplace(P_END_ARROW, "", s);
            s = regexReplace(P_BODY_TO_END, "<$1>", s);
            s = regexReplace(P_XML_CONTENT, "$1<$2", s);
 
        } else {
            //
            // escape stray brackets
            //
            s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
            s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", s);
 
            //
            // the last regexp causes '<>' entities to appear
            // (we need to do a lookahead assertion so that the last bracket can
            // be used in the next pass of the regexp)
            //
            s = regexReplace(P_BOTH_ARROWS, "", s);
        }
 
        return s;
    }
 
    private String checkTags(String s) {
        Matcher m = P_TAGS.matcher(s);
 
        final StringBuffer buf = new StringBuffer();
        while (m.find()) {
            String replaceStr = m.group(1);
            replaceStr = processTag(replaceStr);
            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
        }
        m.appendTail(buf);
 
        s = buf.toString();
 
        // these get tallied in processTag
        // (remember to reset before subsequent calls to filter method)
        for (String key : vTagCounts.keySet()) {
            for (int ii = 0; ii < vTagCounts.get(key); ii++) {
                s += "</" + key + ">";
            }
        }
 
        return s;
    }
 
    private String processRemoveBlanks(final String s) {
        String result = s;
        for (String tag : vRemoveBlanks) {
            if(!P_REMOVE_PAIR_BLANKS.containsKey(tag)){
                P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]\*)?></" + tag + ">"));
            }
            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
            if(!P_REMOVE_SELF_BLANKS.containsKey(tag)){
                P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]\*)?/>"));
            }
            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
        }
 
        return result;
    }
 
    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
        Matcher m = regex_pattern.matcher(s);
        return m.replaceAll(replacement);
    }
 
    private String processTag(final String s) {
        // ending tags
        Matcher m = P_END_TAG.matcher(s);
        if (m.find()) {
            final String name = m.group(1).toLowerCase();
            if (allowed(name)) {
                if (!inArray(name, vSelfClosingTags)) {
                    if (vTagCounts.containsKey(name)) {
                        vTagCounts.put(name, vTagCounts.get(name) - 1);
                        return "</" + name + ">";
                    }
                }
            }
        }
 
        // starting tags
        m = P_START_TAG.matcher(s);
        if (m.find()) {
            final String name = m.group(1).toLowerCase();
            final String body = m.group(2);
            String ending = m.group(3);
            if (allowed(name)) {
                String params = "";
 
                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
                final List<String> paramNames = new ArrayList<String>();
                final List<String> paramValues = new ArrayList<String>();
                while (m2.find()) {
                    paramNames.add(m2.group(1)); //([a-z0-9]+)
                    paramValues.add(m2.group(3)); //(.\*?)
                }
                while (m3.find()) {
                    paramNames.add(m3.group(1)); //([a-z0-9]+)
                    paramValues.add(m3.group(3)); //([^\"\\s']+)
                }
 
                String paramName, paramValue;
                for (int ii = 0; ii < paramNames.size(); ii++) {
                    paramName = paramNames.get(ii).toLowerCase();
                    paramValue = paramValues.get(ii);
                    if (allowedAttribute(name, paramName)) {
                        if (inArray(paramName, vProtocolAtts)) {
                            paramValue = processParamProtocol(paramValue);
                        }
                        params += " " + paramName + "=\"" + paramValue + "\"";
                    }
                }
 
                if (inArray(name, vSelfClosingTags)) {
                    ending = " /";
                }
 
                if (inArray(name, vNeedClosingTags)) {
                    ending = "";
                }
 
                if (ending == null || ending.length() < 1) {
                    if (vTagCounts.containsKey(name)) {
                        vTagCounts.put(name, vTagCounts.get(name) + 1);
                    } else {
                        vTagCounts.put(name, 1);
                    }
                } else {
                    ending = " /";
                }
                return "<" + name + params + ending + ">";
            } else {
                return "";
            }
        }
 
        // comments
        m = P_COMMENT.matcher(s);
        if (!stripComment && m.find()) {
            return  "<" + m.group() + ">";
        }
 
        return "";
    }
 
    private String processParamProtocol(String s) {
        s = decodeEntities(s);
        final Matcher m = P_PROTOCOL.matcher(s);
        if (m.find()) {
            final String protocol = m.group(1);
            if (!inArray(protocol, vAllowedProtocols)) {
                // bad protocol, turn into local anchor link instead
                s = "#" + s.substring(protocol.length() + 1, s.length());
                if (s.startsWith("#//")) {
                    s = "#" + s.substring(3, s.length());
                }
            }
        }
 
        return s;
    }
 
    private String decodeEntities(String s) {
        StringBuffer buf = new StringBuffer();
 
        Matcher m = P_ENTITY.matcher(s);
        while (m.find()) {
            final String match = m.group(1);
            final int decimal = Integer.decode(match).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();
 
        buf = new StringBuffer();
        m = P_ENTITY_UNICODE.matcher(s);
        while (m.find()) {
            final String match = m.group(1);
            final int decimal = Integer.valueOf(match, 16).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();
 
        buf = new StringBuffer();
        m = P_ENCODE.matcher(s);
        while (m.find()) {
            final String match = m.group(1);
            final int decimal = Integer.valueOf(match, 16).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();
 
        s = validateEntities(s);


**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618540462)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

接下来我将给各位同学划分一张学习计划表!

学习计划

那么问题又来了,作为萌新小白,我应该先学什么,再学什么?
既然你都问的这么直白了,我就告诉你,零基础应该从什么开始学起:

阶段一:初级网络安全工程师

接下来我将给大家安排一个为期1个月的网络安全初级计划,当你学完后,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web渗透、安全服务、安全分析等岗位;其中,如果你等保模块学的好,还可以从事等保工程师。

综合薪资区间6k~15k

1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)

2、渗透测试基础(1周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等

3、操作系统基础(1周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)

4、计算机网络基础(1周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现

5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固

6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)

那么,到此为止,已经耗时1个月左右。你已经成功成为了一名“脚本小子”。那么你还想接着往下探索吗?

阶段二:中级or高级网络安全工程师(看自己能力)

综合薪资区间15k~30k

7、脚本编程学习(4周)
在网络安全领域。是否具备编程能力是“脚本小子”和真正网络安全工程师的本质区别。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中,想要高效地使用自制的脚本工具来实现各种目的,更是需要拥有编程能力。

零基础入门的同学,我建议选择脚本语言Python/PHP/Go/Java中的一种,对常用库进行编程学习
搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP,IDE强烈推荐Sublime;

Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,没必要看完

用Python编写漏洞的exp,然后写一个简单的网络爬虫

PHP基本语法学习并书写一个简单的博客系统

熟悉MVC架构,并试着学习一个PHP框架或者Python框架 (可选)

了解Bootstrap的布局或者CSS。

阶段三:顶级网络安全工程师

如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

学习资料分享

当然,只给予计划不给予学习资料的行为无异于耍流氓,这里给大家整理了一份【282G】的网络安全工程师从入门到精通的学习资料包,可点击下方二维码链接领取哦。

  • 27
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值