Flask知识点串烧(二)---HTTP基础

 HTTP响应

  1. 视图函数返回的内容即为响应报文中的主题内容。
  2. 当关闭调试模式时,即FLAK_ENV使用默认值production,如果程序出错,Flask会自动返回500错误响应,而在调试模式下则会显示调试信息和错误堆栈。 
  3. 如果想手动返回错误响应,更方便的方法是使用Flask提供的abort函数。

 响应格式

  1. 不同的响应数据格式需要设置不同的MIME类型,MIME类型在首部的Content-Type字段中定义
  2. MIMIE类型是一种用来识别文件类型的机制,它与文件扩展名相对应,可以让客户端区分不同的文件类型,并执行不同的操作。
  3. MIME格式:“类型名/子类型名”;
  4. 使用Flask提供的make_response(响应主体)方法生成响应对象,设置响应对象的mimetype属性以设置MIME类型;
  5. response.headers["Content-Type"]='text/xml'; charset=utf-8也可以设置MIME类型。但是用mimetype不需要设置字符集选项。

常见的数据格式

  1. 纯文本   text/plain
  2. HTML    text/html,最常用的数据格式,也是Flask返回响应的默认数据类型。
  3. XML      application/xml,HTML中的标签用于显示内容,XML中的标签用于定义数据。xml一般作为AJAX请求的响应格式,或者是WEB api的响应格式。
  4. JSON    application/json

JSON响应

  1. JSON的结构基于“键值对的集合”和“有序值列表”,这2中数据结果类似Python中的字典和列表。
  2. Flask通过引入标准库中的json模块为程序提供了json支持,因此可以导入json对象,然后调用dumps方法将字典,列表或元组序列化为json字符串,再使用mimetype修改MIME类型返回JSON响应。
  3. Flask提供的jsonify()函数包装了json的dumps和load()方法,会对我们传入的参数(传入普通参数或关键字参数)
  4. 进行序列化,转换成JSON字符串,然后生成一个响应对象,并设置正确的MIME类型。
  5. Flask在获取请求中的json数据:request.json属性/request.get_json()方法。

来一块Cookie

  1. HTTP是无状态协议。也就是说在一次请求响应结束后,服务器不会留下任何关于对方状态的信息。
  2. Cookie技术通过在请求和响应报文中添加Cookie数据来保存客户端的状态信息。
  3. 在Flask中如果想在响应中添加一个cookie,最方便的方法是使用Response类提供的set_cookie()方法。
  4. set_cookie视图会在生成的响应报文首部中创建一个Set-Cookie字段,即“Set-Cookie: key1=value1;key2=value2;...” 
  5. 当浏览器保存了服务器端设置的cookie后,浏览器再次发送该服务器的请求会自动携带设置的cookie信息,cookie信息存储在请求首部设置的Cookie字段中。
  6. 从cookie中获取值:request.cookies.get("key", "default_value")

 Response类的常用属性和方法

  1. headers 一个Werkzeug的Headers对象,表示响应首部,可以像字典一样操作;
  2. status 状态码,文本类型
  3. status_code 状态码,整型
  4. mimetype MIME类型
  5. set_cookie() 用来设置一个cookie

session:安全的Cookie

  1. 在浏览器中手动添加和修改Cookie是很容易的事,因此需要对敏感的Cookie内容进行加密。
  2. Flask提供的session对象用来将Cookie数据加密存储。
  3. 默认情况下,它会把数据存储在浏览器中一个名为session的Cookie里。
  4. 设置程序秘钥:session通过秘钥(具有一定复杂度和随机性的字符串)对数据进行签名以加密数据。
  5. 秘钥可以通过app.secret_key="secret key"设置,更安全的办法是写入环境变量或保存在.env文件中,然后通过app.secret_key=os.getenv("SECRET_KEY", "secret key") 设置。
  6. 在生产环境中处于安全考虑,必须使用随机生成的秘钥。

使用session模拟用户登录

  1. session对象可以像字典一样操作,例如,我们向session中添加一个logged-in-cookie,将它的值设为True,表示用户已认证。
  2. 当我们使用session对象添加cookie时,数据会使用程序的秘钥对其进行签名,加密后的数据存储在一块名为session的cookie里。
  3. 使用session对象存储的cookie,用户可以看到其加密后的值,但无法修改它。因为session中的内容使用秘钥进行签名,一旦数据被修改,签名的值也会变化。这样在读取时就会验证失败,对应的session值也会随之失效。所以,除非用户知道秘钥,否则无法对session cookie的值进行修改。
  4. 默认情况下,session_cookie会在用户关闭浏览器时删除。通过将session.permanent属性设为True可以将session的有效期延长为Flask.permanent_session_lifetime属性值对应的datetime.timedelta对象,也可通过配置变量PERMANENR_SESSION_LIFETIME设置,默认为31天。
  5. 尽管session对象会对cookie进行签名加密,但这种方式仅能确保session的内容不会被篡改,加密后的数据借助工具仍能被轻易读取。因此决不能在session中存储敏感信息,比如用户密码。    

Flask上下文

  1. 我们可以把程序中的上下文理解为当前环境的快照;
  2. Flask中有2中上下文:应用上下文和请求上下文;应用上下文存储了程序运行所需的信息;当客户端发送请求时,请求上下文就登场了,请求上下文包含了请求中的所有信息,如URL,HTTP方法等。

上下文全局变量

  1. 我们在全局导入时request只是一个普通的Python对象,为什么在处理请求时,视图函数中的request就会自动包含请求的数据?这是因为Flask会在每个请求产生后自动激活当前请求的上下文,激活请求上下文后,request被临时设为全局可访问。而当每个请求结束后,Flask就自动销毁对应的请求上下文。
  2. 在多线程服务器中,在同一时间可能会有多个请求在处理。假设有3个客户端同时向服务器发送请求,这时每个请求都有各自不同的请求报文,所以请求对象也必然是不同的。因此,请求对象只在各自的线程内是全局的。
  3. 应用上下文变量
    1. current_app 指向处理请求的当前应用实例;
    2. 替代Python的全局变量用法,确保仅在当前请求中可用,用于存储全局数据,每次请求都会重设。
  4. 请求上下文
    1. request 封装客户端发出的请求报文数据。
    2. session 用于记住请求间的数据,通过签名的Cookie实现。
  5. 这4个对象都是代理对象,可以调用get_current_object()获取被代理的真实对象。

current_app 当前应用实例:

  1. 既然有了应用实例app,为什么还需要current_app变量?
  2. 在不同的视图函数中,request对象表示和视图函数对应的请求,即当前请求。而程序也会有多个程序实例的情况,为了获取对应的程序实例,而不是固定的某一程序实例,我们就需要使用current_app实例。

g

  1. g存储在应用上下文,而应用上下文会随着每一个请求的进入而激活,随着每一个请求的处理完毕而销毁,所以每次请求都会重设这个值。
  2. 通常使用g结合请求钩子来保存每个请求处理前所需的全局变量,比如当前登入的用户对象,数据库连接等。
  3. g也支持字典类似get()、pop()以及setdefault()方法进行操作。

激活上下文

  1. 以下情况将自动激活应用上下文:
    1. 使用flask run启动程序时;
    2. 使用就得app.run()方法启动程序时;
    3. 使用@app.cli.command()装饰器注册的Flask命令时;
    4. 使用flask shell命令启动python shell时;
    5. 当请求进入时,请求上下文被自动激活,我们可以使用request和session变量,与此同时应用上下文也被自动激活。
  2. 手动激活上下文:
    1. 应用上下文对象使用app.app_context()获取,我们可以使用with语句执行上下文操作;
              >> from app import app
              >> from flask import current_app
              >> with app.app_context():
              ....    current_app.name
              'app'
    2. 或是显式地使用push()方法推送(激活)上下文;在执行完毕时使用pop()方法销毁上下文
              >> from app import app
              >> from flask import current_app
              >> app_ctx = app.app_context()
              >> app_ctx.push()
              >> current_app.name
              'app'
              >> app_ctx.pop()
    3. 通过app.test_request_context(),激活请求上下文:
              >> from app import app
              >> from flask import current_app
              >> with app.test_request_context('/hello'):
              ...    request.method
              'GET'
    4. 也可以使用push()和pop()激活和销毁上下文。

上下文钩子

  1. teardown_appcontext钩子,使用它注册的回调函数会在程序上下文被销毁时调用;
  2. teatdowm_appcontext装饰的回调函数需要接收异常对象作为参数,当请求正常处理时,这个值为None,此函数的返回值将被忽略。

获取上一个界面的URL

  1. HTTP referer:当用户在某个站点单击链接,浏览器向新链接所在服务器发起请求,请求的数据中包含HTTP_REFERER字段,记录了用户所在的原站点URL:return redirect(request.referrer)
  2. 但很多情况下referrer字段会是空值,我们需要添加一个备胎:return redirect(request.referreer or url)
  3. 除了自动从referrer获取,更常见的方式是在URL中手动加入当前页面的URL查询参数,这个查询参数一般命名为next.

对URL进行安全验证

  1. 鉴于refferrer和next查询参数容易被篡改的特性,如果我们部队这些值进行验证,就会形成开放重定向漏洞。
  2. 确保URL安全的关键就是判断URL是否属于程序内部。

使用AJAX技术渲染异步请求

  1. AJAX可以避免每次都渲染整个界面,不仅可以增强用户体验,也降低了服务器的负载。
  2. JQuery提供的请求发送方法:用于发送GET请求的get()方法,用于发送POST请求的post()方法;用于获取json数据的getjson()方法,还有直接用于获取脚本的getscript()方法。这些方法都是基于ajax()方法实现的。
  3. 程序中某些接收AJAX请求的视图并不需要返回数据给客户端,比如用来删除资源的视图。这时我们可以直接返回空值,并将状态码指定为204(表示无内容):return '',204
  4. Jinjia2提供的generate_lorem_ipsum()返回由随机字符组成的虚拟文章。

HTTP服务器端推送
        不论是传统的HTTP请求-响应模式,还是异步的AJAX请求,服务器端始终处于被动的应答状态,只有在客户端发出请求的情况下,服务器端才会响应。这种模式被称为客户端拉取(client pull)。在这种模式下,用户只能通过刷新页面或主动单价加载按钮来拉取数据。但是在趣多关注实行性的场景下,我们需要的通信模式是服务器端主动推送(server push),比如社交网站在导航栏实时显示新题型和私信的数量,用户的在线状态更新,股票行情监控,显示商品库存信息,多人游戏,文档协作等。

常用的HTTP Server Push技术

  1. 传统轮询:在特定的时间间隔内,客户端使用AJAX技术不断向服务器端发起HTTP请求,然后获取新的数据并更新网页;
  2. 长轮询:和传统轮询类似,但是如果服务器端没有返回数据,那就保持连接一直开启,知道有数据时才返回。取回数据后再次发送另一个请求。
  3. Server-Sent Event(SSE):SSE通过H5中的EventSource API来实现。SSE会在客户端和服务器端建立一个单向的通道,客户端监听来自服务器端的数据,而服务器端可以在任何时间发送数据,两者建立类似订阅发布的通信模式。

       轮询(pulling)这类使用AJAX技术模拟服务器端推送的方法实现起来比较简单,但是通常会造成服务器上资源的浪费,增加服务器的负载,而且由于频繁地发送AJAX请求,会让用户的设备浪费更多地电量。SSE效率更高,基本支持所有的主流浏览器,但浏览器通常会限制标签页的连接数量。

H5 WebSocket协议

        在HTML5的API中还包含了一种WebSocket协议,和HTTP不同,它是基于TCP的全双工通信协议。和服务器端推送技术相比,WebSocket实时性更强,而且可以实现双向通信。WebSocket的浏览器兼容性要强于SSE。

WEB安全防范

        对于WEB程序的安全问题,一个首要的原则是:永远不要相信你的用户。大部分WEB安全问题都是因为没有对用户输入的内容进行“消毒”造成的。

  1. 注入攻击
    1. 包含系统命令注入、SQL注入、NoSQL注入、ORM注入等;
    2. SQL注入攻击原理:在编写SQL时,如果直接将用户传入的数据作为参数使用字符串拼接的方式插入到SQL查询中,那么攻击者可以通过SQL语句做任何事:获取敏感数据、删除数据、删除数据库表...;
    3. 在SQL中;用来结束一行语句;“--”用来注释后面的语句;
    4. 主要防范方法:①使用ORM可以一定程度上避免SQL注入问题;②验证输入类型;③参数化查询(使用各类接口库提供的参数化查询方法,如sqlite3:db.execute('SELECT * FROM students WHERE password=?', password))。④转义字符:比如引号,分号和横线等。使用参数化查询时各种接口库会为我们做转义工作。
  2. XSS攻击
    1. XSS(跨站脚本)是注入攻击的一种,攻击者通过将代码注入被攻击者的网站中,用户一旦访问网页便会执行被注入的恶意脚本。XSS攻击主要分为反射型XSS攻击和存储型XSS攻击两类。
    2. 典型的反射型XSS攻击:通过URL注入攻击脚本,只有当用户访问这个URL时才会执行攻击脚本。如:http://localhost:5000/?name=%3Cscript%3Ealert(%27heihei%27);%3C/script%3E
    3. 如果网站A存在XSS漏洞,攻击者将包含攻击代码的链接发送给网站A的用户Jack,当Jack访问这个链接就会执行攻击代码,从而受到攻击。
    4. 存储型XSS,会把攻击代码存到数据库中,任何用户访问包含攻击代码的页面都会被殃及。比如某个网站通过表单接收用户的留言,如果服务器接收数据后未经处理就存储到数据库中,那么用户可以在留言中插入任意的JS代码。例如,攻击者在留言板中加入一行重定向代码:<script>window.location.href="http://attacker.com"</script>用户一旦访问该页面就会执行JS脚本,被重定向到攻击者写入的网站。
    5. 防范措施:
      1. HTML转义:防范XSS攻击最主要的方式是对用户输入内容进行HTML转义,转移后可以确保用户输入的内容在浏览器中作为文本显示,而不是作为代码解析;Jinjia2提供的escape()函数可以对用户输入的数据进行转义,文本中的特殊字符都将被转义为HTML实体,这行文本最终在浏览器中会被显示为文本形式的"<script>alert("Bingo")</script>";
      2. 验证用户输入:除了转义用户输入外,我们还需要对用户的输入数据进行类型验证。如一个转义无法避免的XSS攻击:<a href="{{ url }}">Website</a> ----> <a href="javascript:alert('bingo')">Website</a>;<img src="{{ url }}"> ----> <img src="123" οnerrοr="alert('Bingo!')">;
      3. 对用户输入的数据进行过滤:仅保留少量允许使用的HTML标签,同时还需要注意过滤HTML标签的属性;
  3. CSRF攻击
    1. 攻击原理:CSRF攻击的大致方式如下:某用户登录了A网站,认证信息保存在cookie中。当用户访问攻击者创建的B网站时,攻击者通过在B网站发送一个伪造的请求到A网站服务器上,让A网站服务器误以为请求来自于自己的网站,于是执行相应的操作,该用户的信息遭到了篡改。总结起来就是,攻击者利用用户在浏览器中保存的认证信息,向对应的站点发送伪造请求。
    2. 防范措施:
      1. 正确使用HTTP方法:GET方法属于安全方法,不会改变资源状态,仅用于获取资源。页面中所有可以通过链接发起的请求,都是GET请求;POST方法用于创建、删除和修改资源。在HTML中通过form标签创建表单并设置提交方法为POST,在提交时会创建POST请求。
      2. 使用HTTP referer获取请求来源,但HTTP referer容易被修改和伪造,所以不能作为主要防控措施。
      3. CSRF令牌校验:在客户端页面中加入伪随机数来防御CSRF攻击,这个伪随机数通常被称为csrf token(CSRF令牌)。CSRF令牌在用户向包含表单的页面发起GET请求时创建,并在一定时间内过期,一般情况下攻击者无法获取到这个令牌值,所以我们可以有效地区分出请求的来源是否安全。通常使用扩展实现CSRF令牌的创建和验证工作,比如Flask-WTF。
发布了132 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Geroge_lmx/article/details/104215483