Anti CSRF layer

pull/144/head
Henrique Dias 2016-07-05 17:46:45 +01:00
parent aa79b076ae
commit 37c77a3cee
8 changed files with 86 additions and 9 deletions

File diff suppressed because one or more lines are too long

View File

@ -420,6 +420,10 @@ pre {
box-sizing: border-box;
}
#token {
display: none;
}
/* MATERIAL ICONS */
@ -1103,4 +1107,4 @@ i.spin {
column-count: 1;
column-gap: 0;
}
}
}

View File

@ -2,6 +2,7 @@
const tempID = "_fm_internal_temporary_id"
var selectedItems = [];
var token = "";
/* * * * * * * * * * * * * * * *
* *
@ -115,6 +116,7 @@ var deleteEvent = function(event) {
let request = new XMLHttpRequest();
request.open('DELETE', link);
request.setRequestHeader('Token', token);
request.send();
request.onreadystatechange = function() {
if (request.readyState == 4) {
@ -165,6 +167,11 @@ var RemoveLastDirectoryPartOf = function(url) {
return (arr.join('/'));
}
// Get the current token
var updateToken = function() {
token = document.getElementById("token").innerHTML;
}
/* * * * * * * * * * * * * * * *
* *
* LISTING SPECIFIC FUNCTIONS *
@ -175,6 +182,7 @@ var reloadListing = function() {
let request = new XMLHttpRequest();
request.open('GET', window.location);
request.setRequestHeader('Minimal', 'true');
request.setRequestHeader('Token', token);
request.send();
request.onreadystatechange = function() {
if (request.readyState == 4) {
@ -186,6 +194,7 @@ var reloadListing = function() {
}
}
}
updateToken();
}
// Rename file event
@ -217,6 +226,7 @@ var renameEvent = function(event) {
let request = new XMLHttpRequest();
request.open('PATCH', link);
request.setRequestHeader('Rename-To', newName);
request.setRequestHeader('Token', token);
request.send();
request.onreadystatechange = function() {
if (request.readyState == 4) {
@ -274,6 +284,7 @@ var handleFiles = function(files) {
let request = new XMLHttpRequest();
request.open('POST', window.location.pathname);
request.setRequestHeader("Upload", "true");
request.setRequestHeader('Token', token);
request.send(data);
request.onreadystatechange = function() {
if (request.readyState == 4) {
@ -382,6 +393,7 @@ var newDirEvent = function(event) {
let html = button.changeToLoading();
let request = new XMLHttpRequest();
request.open("POST", window.location);
request.setRequestHeader('Token', token);
request.setRequestHeader('Filename', document.getElementById('newdir').value);
request.send();
request.onreadystatechange = function() {
@ -444,6 +456,7 @@ var searchEvent = function(event) {
let request = new XMLHttpRequest();
request.open('POST', window.location);
request.setRequestHeader('Command', value);
request.setRequestHeader('Token', token);
request.send();
request.onreadystatechange = function() {
if (request.readyState == 4) {
@ -751,6 +764,7 @@ document.addEventListener("editor", (event) => {
let request = new XMLHttpRequest();
request.open("PUT", window.location);
request.setRequestHeader('Kind', kind);
request.setRequestHeader('Token', token);
request.send(JSON.stringify(data));
request.onreadystatechange = function() {
if (request.readyState == 4) {
@ -781,6 +795,9 @@ document.addEventListener("DOMContentLoaded", function(event) {
}
});
// Updates the token
updateToken();
// Enables open, delete and download buttons
document.getElementById("open").addEventListener("click", openEvent);
document.getElementById("delete").addEventListener("click", deleteEvent);

View File

@ -104,6 +104,7 @@
<main>
{{ template "content" .Data }}
<span id="token">{{ .Config.Token }}</span>
</main>
<footer>

View File

@ -1 +1,2 @@
{{ template "content" .Data }}
<span id="token">{{ .Config.Token }}</span>

View File

@ -17,6 +17,7 @@ type Config struct {
BaseURL string
AbsoluteURL string
AddrPath string
Token string // Anti CSRF token
StyleSheet string // Costum stylesheet
FrontMatter string // Default frontmatter to save files in
HugoEnabled bool // Enables the Hugo plugin for File Manager

42
config/secure.go Normal file
View File

@ -0,0 +1,42 @@
package config
import (
"math/rand"
"net/http"
"time"
)
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
// CheckToken checs if current token is the same as the one used in the request
func (c Config) CheckToken(r *http.Request) bool {
token := r.Header.Get("Token")
return c.Token == token
}
// GenerateToken geneerates a new token
func (c *Config) GenerateToken() {
n := rand.Intn(80)
src := rand.NewSource(time.Now().UnixNano())
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
// future reference: http://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
c.Token = string(b)
}

View File

@ -8,6 +8,7 @@
package filemanager
import (
"fmt"
"io"
"io/ioutil"
"log"
@ -59,6 +60,13 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
}
}
// Secure agains CSRF attacks
if r.Method != http.MethodGet {
if !c.CheckToken(r) {
return http.StatusForbidden, nil
}
}
// Route the request depending on the HTTP Method
switch r.Method {
case http.MethodGet:
@ -67,6 +75,9 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
return assets.Serve(w, r, c)
}
c.GenerateToken()
fmt.Println(c.Token)
if !fi.IsDir {
query := r.URL.Query()
if val, ok := query["raw"]; ok && val[0] == "true" {