Skip to content

Instantly share code, notes, and snippets.

@jackiect
Last active November 5, 2016 07:37
Show Gist options
  • Save jackiect/b8aa4b920482107f4906ac351947e118 to your computer and use it in GitHub Desktop.
Save jackiect/b8aa4b920482107f4906ac351947e118 to your computer and use it in GitHub Desktop.
典型web安全问题
  • 给经常用来搜索的字段加索引,也就是那些很多业务场景中都会出现在where和order by下的字段,譬如create_time字段
  • 连表查询时,为join字段建索引
  • 适当建立一些冗余信息,比如招聘所属商家的名字,招聘录用人数,减少join
  • 尽量避免使用null值,因为会占用额外的索引空间且难以优化查询
  • 尽量使用timestamp存储时间
  • OR改写成IN:OR的效率是n级别,IN的效率是log(n)级别,in的个数建议控制在200以内
  • 值分布很稀少的字段不适合建索引,例如”性别”、“状态”、“类型”这种只有两三个值的字段
  • 不用外键,不用UNIQUE,不用函数和触发器,这些都由应用保证约束
  • 在where条件中不做列运算和非运算,因为任何对列的操作都将导致表扫描,索引的功能发挥不出来
  • 使用连表查询替代子查询,特别是IN的字段还是非索引字段
  • 列表数据不要select全表,要使用LIMIT来分页
  • 读写分离
  • 使用explain分析SQL慢的原因,possible_keys 为空的,可以考虑从on或者WHERE语句中选取合适的字段建索引
  • 使用慢查询日志监测执行时间长的SQL(log-slow-queries = [slow_query_log_filename] long_query_time = 5)

读懂EXPLAIN

http://www.dev120.com/archives/50

mysql范式

  • 第三范式:任何非主属性不依赖于其它非主属性,如学生表中不应该包含公寓描述和公寓地址字段,这两个字段的值虽然依赖学生ID,但仅依赖公寓ID就可决定,存在传递依赖,出现层级数据结构

其他

  • 第一范式:属性不可分,即字段不可含有列表和其他复杂结构的数据,如果还有则表明还可以在分。
  • 第二范式:表中的属性必须完全依赖于全部主键,而不是仅依赖部分主键就可决定,即不存在一些字段依赖某一个主键,而另一些字段依赖另一个主键;比如在选课表中包含学生姓名和年龄,因为这两个字段的值并不依赖课程ID,仅依赖学生ID就可决定,出现分散依赖,这样不单会造成不必要的冗余,还会造成更新困难

最早的时候,我们只需要 GET 和 POST 方法,POST 方法的引入也只是为了消除 URL 过长,参数隐藏,上传文件的问题,完全和语义无关。接触到 RESTful 之后,我们开始思考 GET 和 POST 的不同语义

  1. URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作,3端负责具体的表现
  2. URL中只用复数名词,原则上不使用动词
  3. HTTP动词来控制资源的状态扭转、定位
  4. “资源”是处理的核心,现考虑资源后限定动词
  5. “资源”在S/C之间传输的表现形式,通常用JSON,XML传输文本,用JPG,WebP传输图片等
  6. HTTP Status Code传递Server的状态信息,和HTTP协议并无二致, 200 表示成功,500 表示Server内部错误
  7. 为什么:不需要有显式的前端渲染,只需要一套提供对资源服务的接口
  8. 组成
  9. URL root:二级域名还是二级路径的选择
  10. API版本:可以放在URL里面,也可以用HTTP的Header,依据业务场景
  11. URL中的参数用来过滤,有时可以和API路径参数互换
  12. GET/HEAD方法幂等,即不改变资源状态

PATCH vs PUT

  • 简要回答就是:PUT是完整更新,PATCH是部分更新;PATCH方法是新引入的,对PUT方法的补充,用来对已知资源进行局部更新

http://gloveangels.com/restful-http-patch-method/ https://ihower.tw/blog/archives/6483

RESTfull 与HTTP code

“数据流”、“输入输出”

一切的安全问题都体现在“输入输出”上,一切的安全问题都存在于“数据流”的整个过程中。

web安全问题的本质:输入提交的“特殊数据”,在某个层没处理好,被当作该层的指令,在输出的时候,就会出现相应层的安全问题

精彩举例:

  1. 如果在操作系统层上没处理好,比如Linux的Bash环境把“特殊数据”当做指令执行时,就产生了OS命令执行的安全问题,这段“特殊数据”可能长得如下这般: ; rm -rf /;
  1. 如果在存储层的数据库中没处理好,数据库的SQL解析引擎把这个“特殊数据”当做指令执行时,就产生SQL注入这样的安全问题,这段“特殊数据”可能长得如下这般: ' union select user, pwd, 1, 2, 3, 4 from users--
  1. 如果在Web容器层如nginx中没处理好,nginx把“特殊数据”当做指令执行时,可能会产生远程溢出、DoS等各种安全问题,这段“特殊数据”可能长得如下这般: %c0.%c0./%c0.%c0./%c0.%c0./%c0.%c0./%20
  1. 如果在Web开发框架或Web应用层中没处理好,把“特殊数据”当做指令执行时,可能会产生远程命令执行的安全问题,这段“特殊数据”可能长得如下这般: eval($_REQUEST['x']);
  1. 如果在Web前端层中没处理好,浏览器的JS引擎把“特殊数据”当做指令执行时,可能会产生XSS跨站脚本的安全问题,这段“特殊数据”可能长得如下这般: '"><script>alert(/cos is my hero./)</script>

...

@jackiect
Copy link
Author

jackiect commented May 11, 2016

SQL注入

输入-->输出:请求表单或请求查询字符串(数据)中包含了SQL命令(指令),被后台逻辑当成一般请求参数处理,最终达到欺骗服务器执行恶意的SQL命令

典型方式:

更多案例

应对

  1. 输入:不要信任用户的输入,要对用户输入进行校验,如通过正则表达式校验、类型校验(针对有固定格式的变量或字段如整型、邮箱格式、id规则)、过滤特殊符号(针对评论、描述等长文本数据),总之:从用户输入中摘掉SQL指令
  2. 后台:不要使用动态拼装SQL,可以使用参数化的SQL、使用预编译SQL语句或者直接使用存储过程进行数据查询存取
  3. 后台:登陆逻辑中,先通过登录名获取记录,再匹配记录中的密码和用户输入密码
  4. 输出:应用的异常信息应该给出尽可能少的提示或进行异常包装,防止猜测数据库名或其他数据库信息

@jackiect
Copy link
Author

jackiect commented May 18, 2016

CronTab

  • service crond restart

  • /etc/crontab

    01 * * * * root run-parts /etc/cron.hourly/
    02 4 * * * root run-parts /etc/cron.daily/
    22 4 * * 0 root run-parts /etc/cron.weekly/
    42 4 1 * * root run-parts /etc/cron.monthly/
    
  • 用户通过crontab -e命令编辑,一编辑好的crontab文件放在 /var/spool/cron/

 *  *  *  *  *  command
分 时 日 月 周   命令
  • a, b, c 表示第 a, b, c,... 时间点要执行
  • a-b 表示从第 a 时间点到第 b 时间点这段时间内
  • */n 表示每 n 个时间间隔执行一次

@jackiect
Copy link
Author

jackiect commented May 18, 2016

redis作session

  • python redis库redis==2.10.3
  • flask SessionInterface接口,主要重写open_sessionsave_session
  • app.session_interface = RedisSessionInterface(redis.StrictRedis(
    connection_pool=redis.ConnectionPool(
    max_connections=config.redis_max_connections,
    **config.redis_config
    )))
  • 自定义

class RedisSession(CallbackDict, SessionMixin):
    def __init__(self, initial=None, sid=None, new=False):
        def on_update(self):
            self.modified = True
        CallbackDict.__init__(self, initial, on_update)
        self.sid = sid
        self.new = new
        self.modified = False


class RedisSessionInterface(SessionInterface):
    serializer = pickle
    session_class = RedisSession

    def __init__(self, redis=None, prefix='session:'):
        if redis is None:
            redis = Redis()

        self.redis = redis
        self.prefix = prefix

    def generate_sid(self):
        # return str(uuid4())
        chars = string.letters + string.digits + "_-"
        return "".join([chars[ord(i) % len(chars)] for i in os.urandom(40)])

    def get_redis_expiration_time(self, app, session):
        if session.permanent:
            return app.permanent_session_lifetime
        return timedelta(hours=1)

    def open_session(self, app, request):
        sid = request.cookies.get(app.session_cookie_name)
        if not sid:
            sid = self.generate_sid()
            return self.session_class(sid=sid, new=True)
        val = self.redis.get(self.prefix + sid)
        if val is not None:
            data = self.serializer.loads(val)
            return self.session_class(data, sid=sid)
        return self.session_class(sid=sid, new=True)

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        if not session:
            self.redis.delete(self.prefix + session.sid)
            if session.modified:
                response.delete_cookie(app.session_cookie_name, domain=domain)
            return

        redis_exp = self.get_redis_expiration_time(app, session)
        if isinstance(redis_exp, timedelta):
            redis_exp = total_seconds(redis_exp)

        val = self.serializer.dumps(dict(session))
        self.redis.setex(self.prefix + session.sid, redis_exp, val)

        httponly = self.get_cookie_httponly(app)
        cookie_exp = self.get_expiration_time(app, session)

        response.set_cookie(app.session_cookie_name, session.sid,
            expires=cookie_exp, httponly=httponly, domain=domain)

  • app.session_interface = ItsdangerousSessionInterface()

@jackiect
Copy link
Author

jackiect commented May 20, 2016

Redis

存储Json是用string还是hash

用String:

  • 大量的访问是需要完整字段信息
  • 不同的纪录在字段结构上差异很大,如结伴应聘、连报多天的应聘和普通应聘不一样

用Hash

  • 一小部分字段访问量或查询量较大
  • 明确的知道存储的Json是固定的字段结构

redis作为http session

  • 如果对会话丢失比较敏感,可以配置appendfsync always,保证每次操作都持久到文件中
  • 如果对及时反馈要求高,允许一定的容错,可以配置appendfsync everysec,每秒持久化一次
  • redis续期session,用户每次访问都要将用户的注销时间推迟一定时间如30分钟

@jackiect
Copy link
Author

jackiect commented May 20, 2016

Cookies劫持

  • 设置cookie为HttpOnly
  • 按照浏览器的UA信息或者是客户端的IP地址来生成一个签名串,保证正确的网络环境
  • 每个请求里面加上token,保证用户请求的唯一性。

@jackiect
Copy link
Author

面试中的linux命令

  • 查找文件:findfind /home/ljianhui -user ljianhui
  • grep:按行查找文本文件或管道,ls -l | grep -i d
  • ps:查看系统进程运行情况;ps aux查看系统所有的进程数据
  • netstat -anp:查看端口打开情况

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment