Demo 学 Echo Part13 跨站点请求伪造(CSRF)

跨站点请求伪造(CSRF)攻击以及如何预测它们
此类攻击的示例:尝试通过Web浏览器以外的媒体登录,例如使用CURL等
通常的防御方法是使用csrf令牌。在有表单的每个页面上,都会生成csrf令牌。提交表单时,会在请求中插入CSRF,然后后端检查发送的CSRF是否有效
csrf令牌本身是每次表单页面出现时生成的随机字符串。通常在每个POST请求中,令牌都作为标头,数据表单或查询字符串插入
package main
import (
"fmt"
"html/template"
"net/http"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
type M map[string]interface{}
func main() {
tmpl := template.Must(template.ParseGlob("./*.html"))
e := echo.New()
const CSRF_TOKEN_HEADER = "X-Csrf-Token"
const CSRF_KEY = "csrf_token"
e.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
TokenLookup: "header:" + CSRF_TOKEN_HEADER,
ContextKey: CSRF_KEY,
}))
e.GET("/index", func(c echo.Context) error {
data := make(M)
data[CSRF_KEY] = c.Get(CSRF_KEY)
return tmpl.ExecuteTemplate(c.Response(), "view.html", data)
})
//sayhello处理程序中没有csrf标记检查,因为它已被中间件隐式处理
e.POST("/sayhello", func(c echo.Context) error {
data := make(M)
if err := c.Bind(&data); err != nil {
return err
}
message := fmt.Sprintf("hello %s", data["name"])
return c.JSON(http.StatusOK, message)
})
e.Logger.Fatal(e.Start(":9000"))
}
view.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form id="form" action="/sayhello" method="POST">
<div>
<label>Name</label>
<input type="text" name="name" placeholder="Type your name here">
</div>
<div>
<label>Gender</label>
<select name="gender">
<option value="">Select one</option>
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</div>
<div>
<input type="hidden" name="csrf_token" value="{{ .csrf_token }}">
<button type="submit">Submit</button>
</div>
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function () {
$('form').on('submit', function (e) {
e.preventDefault()
var self = $(this)
var formData = {
name: self.find('[name="name"]').val(),
gender: self.find('[name="gender"]').val(),
}
var url = self.attr('action')
var method = self.attr('method')
var payload = JSON.stringify(formData)
$.ajax({
url: url,
type: method,
contentType: 'application/json',
data: payload,
beforeSend: function(req) {
var csrfToken = self.find('[name=csrf_token]').val()
req.setRequestHeader("X-Csrf-Token", csrfToken)
},
}).then(function (res) {
alert(res)
}).catch(function (err) {
alert('ERROR: ' + err.responseText)
console.log('err', err)
})
})
})
</script>
</body>
</html>
浏览器访问,提交数据,一切正常:
如果把JavaScript中的以下去掉,会验证不过的
beforeSend: function(req) {
var csrfToken = self.find('[name=csrf_token]').val()
req.setRequestHeader("X-Csrf-Token", csrfToken)
},
尝试直接CURL访问,验证肯定不过
curl -X POST http://localhost:9000/sayhello \
-H 'Content-Type: application/json' \
-d '{"name":"noval","gender":"male"}'
{"message":"invalid csrf token"}
curl -X POST http://localhost:9000/sayhello \
-d 'name=Joe' \
-d 'gender=male'
{"message":"invalid csrf token"}
最后修改于 2019-08-17