Add en/0.6.x.md syntax highlighting
This commit is contained in:
20
en/06.1.md
20
en/06.1.md
@@ -35,11 +35,11 @@ If your application does set an expiry time (for example, setMaxAge(60*60*24)),
|
|||||||
## Set cookies in Go
|
## Set cookies in Go
|
||||||
|
|
||||||
Go uses the `SetCookie` function in the `net/http` package to set cookies:
|
Go uses the `SetCookie` function in the `net/http` package to set cookies:
|
||||||
|
```Go
|
||||||
http.SetCookie(w ResponseWriter, cookie *Cookie)
|
http.SetCookie(w ResponseWriter, cookie *Cookie)
|
||||||
|
```
|
||||||
`w` is the response of the request and cookie is a struct. Let's see what it looks like:
|
`w` is the response of the request and cookie is a struct. Let's see what it looks like:
|
||||||
|
```Go
|
||||||
type Cookie struct {
|
type Cookie struct {
|
||||||
Name string
|
Name string
|
||||||
Value string
|
Value string
|
||||||
@@ -57,27 +57,27 @@ Go uses the `SetCookie` function in the `net/http` package to set cookies:
|
|||||||
Raw string
|
Raw string
|
||||||
Unparsed []string // Raw text of unparsed attribute-value pairs
|
Unparsed []string // Raw text of unparsed attribute-value pairs
|
||||||
}
|
}
|
||||||
|
```
|
||||||
Here is an example of setting a cookie:
|
Here is an example of setting a cookie:
|
||||||
|
```Go
|
||||||
expiration := time.Now().Add(365 * 24 * time.Hour)
|
expiration := time.Now().Add(365 * 24 * time.Hour)
|
||||||
cookie := http.Cookie{Name: "username", Value: "astaxie", Expires: expiration}
|
cookie := http.Cookie{Name: "username", Value: "astaxie", Expires: expiration}
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
|
```
|
||||||
|
|
||||||
## Fetch cookies in Go
|
## Fetch cookies in Go
|
||||||
|
|
||||||
The above example shows how to set a cookie. Now let's see how to get a cookie that has been set:
|
The above example shows how to set a cookie. Now let's see how to get a cookie that has been set:
|
||||||
|
```Go
|
||||||
cookie, _ := r.Cookie("username")
|
cookie, _ := r.Cookie("username")
|
||||||
fmt.Fprint(w, cookie)
|
fmt.Fprint(w, cookie)
|
||||||
|
```
|
||||||
Here is another way to get a cookie:
|
Here is another way to get a cookie:
|
||||||
|
```Go
|
||||||
for _, cookie := range r.Cookies() {
|
for _, cookie := range r.Cookies() {
|
||||||
fmt.Fprint(w, cookie.Name)
|
fmt.Fprint(w, cookie.Name)
|
||||||
}
|
}
|
||||||
|
```
|
||||||
As you can see, it's very convenient to get cookies from requests.
|
As you can see, it's very convenient to get cookies from requests.
|
||||||
|
|
||||||
## Sessions
|
## Sessions
|
||||||
|
|||||||
46
en/06.2.md
46
en/06.2.md
@@ -34,7 +34,7 @@ Next, we'll examine a complete example of a Go session manager and the rationale
|
|||||||
### Session manager
|
### Session manager
|
||||||
|
|
||||||
Define a global session manager:
|
Define a global session manager:
|
||||||
|
```Go
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
cookieName string //private cookiename
|
cookieName string //private cookiename
|
||||||
lock sync.Mutex // protects session
|
lock sync.Mutex // protects session
|
||||||
@@ -49,40 +49,40 @@ Define a global session manager:
|
|||||||
}
|
}
|
||||||
return &Manager{provider: provider, cookieName: cookieName, maxlifetime: maxlifetime}, nil
|
return &Manager{provider: provider, cookieName: cookieName, maxlifetime: maxlifetime}, nil
|
||||||
}
|
}
|
||||||
|
```
|
||||||
Create a global session manager in the `main()` function:
|
Create a global session manager in the `main()` function:
|
||||||
|
```Go
|
||||||
var globalSessions *session.Manager
|
var globalSessions *session.Manager
|
||||||
// Then, initialize the session manager
|
// Then, initialize the session manager
|
||||||
func init() {
|
func init() {
|
||||||
globalSessions = NewManager("memory","gosessionid",3600)
|
globalSessions = NewManager("memory","gosessionid",3600)
|
||||||
}
|
}
|
||||||
|
```
|
||||||
We know that we can save sessions in many ways including in memory, the file system or directly into the database. We need to define a `Provider` interface in order to represent the underlying structure of our session manager:
|
We know that we can save sessions in many ways including in memory, the file system or directly into the database. We need to define a `Provider` interface in order to represent the underlying structure of our session manager:
|
||||||
|
```Go
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
SessionInit(sid string) (Session, error)
|
SessionInit(sid string) (Session, error)
|
||||||
SessionRead(sid string) (Session, error)
|
SessionRead(sid string) (Session, error)
|
||||||
SessionDestroy(sid string) error
|
SessionDestroy(sid string) error
|
||||||
SessionGC(maxLifeTime int64)
|
SessionGC(maxLifeTime int64)
|
||||||
}
|
}
|
||||||
|
```
|
||||||
- `SessionInit` implements the initialization of a session, and returns a new session if it succeeds.
|
- `SessionInit` implements the initialization of a session, and returns a new session if it succeeds.
|
||||||
- `SessionRead` returns a session represented by the corresponding sid. Creates a new session and returns it if it does not already exist.
|
- `SessionRead` returns a session represented by the corresponding sid. Creates a new session and returns it if it does not already exist.
|
||||||
- `SessionDestroy` given an sid, deletes the corresponding session.
|
- `SessionDestroy` given an sid, deletes the corresponding session.
|
||||||
- `SessionGC` deletes expired session variables according to `maxLifeTime`.
|
- `SessionGC` deletes expired session variables according to `maxLifeTime`.
|
||||||
|
|
||||||
So what methods should our session interface have? If you have any experience in web development, you should know that there are only four operations for sessions: set value, get value, delete value and get current session id. So, our session interface should have four methods to perform these operations.
|
So what methods should our session interface have? If you have any experience in web development, you should know that there are only four operations for sessions: set value, get value, delete value and get current session id. So, our session interface should have four methods to perform these operations.
|
||||||
|
```Go
|
||||||
type Session interface {
|
type Session interface {
|
||||||
Set(key, value interface{}) error //set session value
|
Set(key, value interface{}) error //set session value
|
||||||
Get(key interface{}) interface{} //get session value
|
Get(key interface{}) interface{} //get session value
|
||||||
Delete(key interface{}) error //delete session value
|
Delete(key interface{}) error //delete session value
|
||||||
SessionID() string //back current sessionID
|
SessionID() string //back current sessionID
|
||||||
}
|
}
|
||||||
|
```
|
||||||
This design takes its roots from the `database/sql/driver`, which defines the interface first, then registers specific structures when we want to use it. The following code is the internal implementation of a session register function.
|
This design takes its roots from the `database/sql/driver`, which defines the interface first, then registers specific structures when we want to use it. The following code is the internal implementation of a session register function.
|
||||||
|
```Go
|
||||||
var provides = make(map[string]Provider)
|
var provides = make(map[string]Provider)
|
||||||
|
|
||||||
// Register makes a session provider available by the provided name.
|
// Register makes a session provider available by the provided name.
|
||||||
@@ -97,11 +97,11 @@ This design takes its roots from the `database/sql/driver`, which defines the in
|
|||||||
}
|
}
|
||||||
provides[name] = provider
|
provides[name] = provider
|
||||||
}
|
}
|
||||||
|
```
|
||||||
### Unique session id's
|
### Unique session id's
|
||||||
|
|
||||||
Session id's are for identifying users of web applications, so they must be unique. The following code shows how to achieve this goal:
|
Session id's are for identifying users of web applications, so they must be unique. The following code shows how to achieve this goal:
|
||||||
|
```Go
|
||||||
func (manager *Manager) sessionId() string {
|
func (manager *Manager) sessionId() string {
|
||||||
b := make([]byte, 32)
|
b := make([]byte, 32)
|
||||||
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
||||||
@@ -109,11 +109,11 @@ Session id's are for identifying users of web applications, so they must be uniq
|
|||||||
}
|
}
|
||||||
return base64.URLEncoding.EncodeToString(b)
|
return base64.URLEncoding.EncodeToString(b)
|
||||||
}
|
}
|
||||||
|
```
|
||||||
### Creating a session
|
### Creating a session
|
||||||
|
|
||||||
We need to allocate or get an existing session in order to validate user operations. The `SessionStart` function is for checking the existence of any sessions related to the current user, and creating a new session if none is found.
|
We need to allocate or get an existing session in order to validate user operations. The `SessionStart` function is for checking the existence of any sessions related to the current user, and creating a new session if none is found.
|
||||||
|
```Go
|
||||||
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session Session) {
|
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session Session) {
|
||||||
manager.lock.Lock()
|
manager.lock.Lock()
|
||||||
defer manager.lock.Unlock()
|
defer manager.lock.Unlock()
|
||||||
@@ -129,9 +129,9 @@ We need to allocate or get an existing session in order to validate user operati
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
```
|
||||||
Here is an example that uses sessions for a login operation.
|
Here is an example that uses sessions for a login operation.
|
||||||
|
```Go
|
||||||
func login(w http.ResponseWriter, r *http.Request) {
|
func login(w http.ResponseWriter, r *http.Request) {
|
||||||
sess := globalSessions.SessionStart(w, r)
|
sess := globalSessions.SessionStart(w, r)
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
@@ -144,13 +144,13 @@ Here is an example that uses sessions for a login operation.
|
|||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", 302)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
### Operation value: set, get and delete
|
### Operation value: set, get and delete
|
||||||
|
|
||||||
The `SessionStart` function returns a variable that implements a session interface. How do we use it?
|
The `SessionStart` function returns a variable that implements a session interface. How do we use it?
|
||||||
|
|
||||||
You saw `session.Get("uid")` in the above example for a basic operation. Now let's examine a more detailed example.
|
You saw `session.Get("uid")` in the above example for a basic operation. Now let's examine a more detailed example.
|
||||||
|
```Go
|
||||||
func count(w http.ResponseWriter, r *http.Request) {
|
func count(w http.ResponseWriter, r *http.Request) {
|
||||||
sess := globalSessions.SessionStart(w, r)
|
sess := globalSessions.SessionStart(w, r)
|
||||||
createtime := sess.Get("createtime")
|
createtime := sess.Get("createtime")
|
||||||
@@ -170,7 +170,7 @@ You saw `session.Get("uid")` in the above example for a basic operation. Now let
|
|||||||
w.Header().Set("Content-Type", "text/html")
|
w.Header().Set("Content-Type", "text/html")
|
||||||
t.Execute(w, sess.Get("countnum"))
|
t.Execute(w, sess.Get("countnum"))
|
||||||
}
|
}
|
||||||
|
```
|
||||||
As you can see, operating on sessions simply involves using the key/value pattern in the Set, Get and Delete operations.
|
As you can see, operating on sessions simply involves using the key/value pattern in the Set, Get and Delete operations.
|
||||||
|
|
||||||
Because sessions have the concept of an expiry time, we define the GC to update the session's latest modify time. This way, the GC will not delete sessions that have expired but are still being used.
|
Because sessions have the concept of an expiry time, we define the GC to update the session's latest modify time. This way, the GC will not delete sessions that have expired but are still being used.
|
||||||
@@ -178,8 +178,8 @@ Because sessions have the concept of an expiry time, we define the GC to update
|
|||||||
### Reset sessions
|
### Reset sessions
|
||||||
|
|
||||||
We know that web applications have a logout operation. When users logout, we need to delete the corresponding session. We've already used the reset operation in above example -now let's take a look at the function body.
|
We know that web applications have a logout operation. When users logout, we need to delete the corresponding session. We've already used the reset operation in above example -now let's take a look at the function body.
|
||||||
|
```Go
|
||||||
//Destroy sessionid
|
// Destroy sessionid
|
||||||
func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request){
|
func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request){
|
||||||
cookie, err := r.Cookie(manager.cookieName)
|
cookie, err := r.Cookie(manager.cookieName)
|
||||||
if err != nil || cookie.Value == "" {
|
if err != nil || cookie.Value == "" {
|
||||||
@@ -193,11 +193,11 @@ We know that web applications have a logout operation. When users logout, we nee
|
|||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
### Delete sessions
|
### Delete sessions
|
||||||
|
|
||||||
Let's see how to let the session manager delete a session. We need to start the GC in the `main()` function:
|
Let's see how to let the session manager delete a session. We need to start the GC in the `main()` function:
|
||||||
|
```Go
|
||||||
func init() {
|
func init() {
|
||||||
go globalSessions.GC()
|
go globalSessions.GC()
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ Let's see how to let the session manager delete a session. We need to start the
|
|||||||
manager.provider.SessionGC(manager.maxlifetime)
|
manager.provider.SessionGC(manager.maxlifetime)
|
||||||
time.AfterFunc(time.Duration(manager.maxlifetime), func() { manager.GC() })
|
time.AfterFunc(time.Duration(manager.maxlifetime), func() { manager.GC() })
|
||||||
}
|
}
|
||||||
|
```
|
||||||
We see that the GC makes full use of the timer function in the `time` package. It automatically calls GC when the session times out, ensuring that all sessions are usable during `maxLifeTime`. A similar solution can be used to count online users.
|
We see that the GC makes full use of the timer function in the `time` package. It automatically calls GC when the session times out, ensuring that all sessions are usable during `maxLifeTime`. A similar solution can be used to count online users.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|||||||
12
en/06.3.md
12
en/06.3.md
@@ -1,7 +1,7 @@
|
|||||||
# 6.3 Session storage
|
# 6.3 Session storage
|
||||||
|
|
||||||
We introduced a simple session manager's working principles in the previous section, and among other things, we defined a session storage interface. In this section, I'm going to show you an example of a memory based session storage engine that implements this interface. You can tailor this to other forms of session storage as well.
|
We introduced a simple session manager's working principles in the previous section, and among other things, we defined a session storage interface. In this section, I'm going to show you an example of a memory based session storage engine that implements this interface. You can tailor this to other forms of session storage as well.
|
||||||
|
```Go
|
||||||
package memory
|
package memory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -114,16 +114,16 @@ We introduced a simple session manager's working principles in the previous sect
|
|||||||
session.Register("memory", pder)
|
session.Register("memory", pder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
```
|
||||||
The above example implements a memory based session storage mechanism. It uses its `init()` function to register this storage engine to the session manager. So how do we register this engine from our main program?
|
The above example implements a memory based session storage mechanism. It uses its `init()` function to register this storage engine to the session manager. So how do we register this engine from our main program?
|
||||||
|
```Go
|
||||||
import (
|
import (
|
||||||
"github.com/astaxie/session"
|
"github.com/astaxie/session"
|
||||||
_ "github.com/astaxie/session/providers/memory"
|
_ "github.com/astaxie/session/providers/memory"
|
||||||
)
|
)
|
||||||
|
```
|
||||||
We use the blank import mechanism (which will invoke the package's `init()` function automatically) to register this engine to a session manager. We then use the following code to initialize the session manager:
|
We use the blank import mechanism (which will invoke the package's `init()` function automatically) to register this engine to a session manager. We then use the following code to initialize the session manager:
|
||||||
|
```Go
|
||||||
var globalSessions *session.Manager
|
var globalSessions *session.Manager
|
||||||
|
|
||||||
// initialize in init() function
|
// initialize in init() function
|
||||||
@@ -131,7 +131,7 @@ We use the blank import mechanism (which will invoke the package's `init()` func
|
|||||||
globalSessions, _ = session.NewManager("memory", "gosessionid", 3600)
|
globalSessions, _ = session.NewManager("memory", "gosessionid", 3600)
|
||||||
go globalSessions.GC()
|
go globalSessions.GC()
|
||||||
}
|
}
|
||||||
|
```
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
- [Directory](preface.md)
|
- [Directory](preface.md)
|
||||||
|
|||||||
12
en/06.4.md
12
en/06.4.md
@@ -7,7 +7,7 @@ In this section, we are going to show you how to hijack a session for educationa
|
|||||||
## The session hijacking process
|
## The session hijacking process
|
||||||
|
|
||||||
The following code is a counter for the `count` variable:
|
The following code is a counter for the `count` variable:
|
||||||
|
```Go
|
||||||
func count(w http.ResponseWriter, r *http.Request) {
|
func count(w http.ResponseWriter, r *http.Request) {
|
||||||
sess := globalSessions.SessionStart(w, r)
|
sess := globalSessions.SessionStart(w, r)
|
||||||
ct := sess.Get("countnum")
|
ct := sess.Get("countnum")
|
||||||
@@ -20,7 +20,7 @@ The following code is a counter for the `count` variable:
|
|||||||
w.Header().Set("Content-Type", "text/html")
|
w.Header().Set("Content-Type", "text/html")
|
||||||
t.Execute(w, sess.Get("countnum"))
|
t.Execute(w, sess.Get("countnum"))
|
||||||
}
|
}
|
||||||
|
```
|
||||||
The content of `count.gtpl` is as follows:
|
The content of `count.gtpl` is as follows:
|
||||||
|
|
||||||
Hi. Now count:{{.}}
|
Hi. Now count:{{.}}
|
||||||
@@ -60,7 +60,7 @@ Through this simple example of hijacking a session, you can see that it's very d
|
|||||||
The first step is to only set session id's in cookies, instead of in URL rewrites. Also, we should set the httponly cookie property to true. This restricts client-side scripts from gaining access to the session id. Using these techniques, cookies cannot be accessed by XSS and it won't be as easy as we demonstrated to get a session id from a cookie manager.
|
The first step is to only set session id's in cookies, instead of in URL rewrites. Also, we should set the httponly cookie property to true. This restricts client-side scripts from gaining access to the session id. Using these techniques, cookies cannot be accessed by XSS and it won't be as easy as we demonstrated to get a session id from a cookie manager.
|
||||||
|
|
||||||
The second step is to add a token to every request. Similar to the manner in which we dealt with repeating form submissions in previous sections, we add a hidden field that contains a token. When a request is sent to the server, we can verify this token to prove that the request is unique.
|
The second step is to add a token to every request. Similar to the manner in which we dealt with repeating form submissions in previous sections, we add a hidden field that contains a token. When a request is sent to the server, we can verify this token to prove that the request is unique.
|
||||||
|
```Go
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
salt:="astaxie%^7&8888"
|
salt:="astaxie%^7&8888"
|
||||||
io.WriteString(h,salt+time.Now().String())
|
io.WriteString(h,salt+time.Now().String())
|
||||||
@@ -69,11 +69,11 @@ The second step is to add a token to every request. Similar to the manner in whi
|
|||||||
// ask to log in
|
// ask to log in
|
||||||
}
|
}
|
||||||
sess.Set("token",token)
|
sess.Set("token",token)
|
||||||
|
```
|
||||||
### Session id timeout
|
### Session id timeout
|
||||||
|
|
||||||
Another solution is to add a create time for every session, and to replace expired session id's with new ones. This can prevent session hijacking under certain circumstances such as when the hijack is attempted too late.
|
Another solution is to add a create time for every session, and to replace expired session id's with new ones. This can prevent session hijacking under certain circumstances such as when the hijack is attempted too late.
|
||||||
|
```Go
|
||||||
createtime := sess.Get("createtime")
|
createtime := sess.Get("createtime")
|
||||||
if createtime == nil {
|
if createtime == nil {
|
||||||
sess.Set("createtime", time.Now().Unix())
|
sess.Set("createtime", time.Now().Unix())
|
||||||
@@ -81,7 +81,7 @@ Another solution is to add a create time for every session, and to replace expir
|
|||||||
globalSessions.SessionDestroy(w, r)
|
globalSessions.SessionDestroy(w, r)
|
||||||
sess = globalSessions.SessionStart(w, r)
|
sess = globalSessions.SessionStart(w, r)
|
||||||
}
|
}
|
||||||
|
```
|
||||||
We set a value to save the create time and check if it's expired (I set 60 seconds here). This step can often thwart session hijacking attempts.
|
We set a value to save the create time and check if it's expired (I set 60 seconds here). This step can often thwart session hijacking attempts.
|
||||||
|
|
||||||
By combining the two solutions set out above you will be able to prevent most session hijacking attempts from succeeding. On the one hand, session id's that are frequently reset will result in an attacker always getting expired and useless session id's; on the other hand, by setting the httponly property on cookies and ensuring that session id's can only be passed via cookies, all URL based attacks are mitigated. Finally, we set `MaxAge=0` on our cookies, which means that the session id's will not be saved in the browser history.
|
By combining the two solutions set out above you will be able to prevent most session hijacking attempts from succeeding. On the one hand, session id's that are frequently reset will result in an attacker always getting expired and useless session id's; on the other hand, by setting the httponly property on cookies and ensuring that session id's can only be passed via cookies, all URL based attacks are mitigated. Finally, we set `MaxAge=0` on our cookies, which means that the session id's will not be saved in the browser history.
|
||||||
|
|||||||
Reference in New Issue
Block a user