Skip to content

Commit

Permalink
Added session config
Browse files Browse the repository at this point in the history
  • Loading branch information
TarradeMarc committed Mar 27, 2024
1 parent b11c2bb commit a8f4ed4
Show file tree
Hide file tree
Showing 16 changed files with 321 additions and 51 deletions.
1 change: 1 addition & 0 deletions configmanager/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ RUN npm install hsts
COPY ./server.js /app/server.js
RUN mkdir /data
COPY ./cad-default.json /data/cad-default.json
COPY ./session.json /data/session.json
USER nobody
CMD ["node", "server.js"]
43 changes: 35 additions & 8 deletions configmanager/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ app.get('/:namespace/:application', (req, res) => {
const { namespace, application } = req.params;
const filePath = path.resolve(path.normalize(`${__dirname}/data/cad-${namespace}-${application}.json`).replace(/^(\.\.(\/|\\|$))+/, ''));
const defaultFilePath = `/data/cad-default.json`;
const sessionFilePath = `/data/session.json`;
if(!filePath.startsWith(__dirname)){
return res.end()
}
Expand All @@ -30,27 +31,53 @@ app.get('/:namespace/:application', (req, res) => {
res.json({});
} else {
// If the file exists, read its contents and return as JSON object
fs.readFile(defaultFilePath, 'utf8', (err, data) => {
fs.readFile(defaultFilePath, 'utf8', (err, decoys) => {
if (err) {
console.warn("Default decoy config file is missing !");
return res.json([]);
}
if(!data) return res.json([])
const json = JSON.parse(data);
res.json(json);
if(!decoys) return res.json([])
const decoysJson = JSON.parse(decoys);
// Check if the file exists
fs.access(sessionFilePath, fs.constants.F_OK, err => {
if(err) return res.json({ decoys: decoysJson });

// If the file exists, read its contents and return as JSON object
fs.readFile(sessionFilePath, 'utf8', (err, session) => {
if(err) return res.json({ decoys: decoysJson });
if (session) {
const sessionJson = JSON.parse(session);
return res.json({ decoy: decoysJson, session: sessionJson });
}
return res.json({ decoy: decoysJson })
})
})
});
}
});
} else {
// If the file exists, read its contents and return as JSON object
fs.readFile(filePath, 'utf8', (err, data) => {
fs.readFile(filePath, 'utf8', (err, decoys) => {
if (err) {
console.warn("Decoy config file is missing !");
return res.json([]);
}
if(!data) return res.json([])
const json = JSON.parse(data);
res.json(json);
if(!decoys) return res.json([])
const decoysJson = JSON.parse(decoys);
// Check if the file exists
fs.access(sessionFilePath, fs.constants.F_OK, err => {
if(err) return res.json({ decoys: decoysJson });

// If the file exists, read its contents and return as JSON object
fs.readFile(sessionFilePath, 'utf8', (err, session) => {
if(err) return res.json({ decoys: decoysJson });
if (session) {
const sessionJson = JSON.parse(session);
return res.json({ decoy: decoysJson, session: sessionJson });
}
return res.json({ decoy: decoysJson })
})
})
});
}
});
Expand Down
12 changes: 12 additions & 0 deletions configmanager/session.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"session": {
"in": "",
"key": ""

},
"username": {
"in": "",
"key": "",
"value": ""
}
}
10 changes: 10 additions & 0 deletions proxy/wasm/alert/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type Alert struct {
Server string
// attributes for external attribution
SourceIP string
Authenticated bool //Is the user autenticated
Session string // username of the user
Useragent string
// request details
Path string
Expand Down Expand Up @@ -76,6 +78,14 @@ func SendAlert(filter *config_parser.FilterType, logParameters map[string]string
DecoyInjectedValue: logParameters["injected"],
Severity: filter.Detect.Alert.Severity,
}
if logParameters["authenticated"] != "" {
alertContent.Authenticated = true
} else if _, exists := logParameters["authenticated"]; exists {
alertContent.Authenticated = false
}
if logParameters["username"] != "" {
alertContent.Session = logParameters["username"]
}

jsonAlertContent, _ := json.MarshalIndent(&alertContent, "", " ")
proxywasm.LogWarnf("\n!!!!! ALERT !!!!! DECOY TRIGGERED !!!!!\n %v", string(jsonAlertContent))
Expand Down
Binary file modified proxy/wasm/cloud-active-defense.wasm
Binary file not shown.
12 changes: 12 additions & 0 deletions proxy/wasm/config_parser/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,16 @@ const (
LOW = "LOW"
)

type InSession string
const (
cookie_inS InSession = "cookie"
header_inS = "header"
)

type InUsername string
const (
cookie_inU InUsername = "cookie"
header_inU = "header"
payload_inU = "payload"
)
/* **** */
21 changes: 17 additions & 4 deletions proxy/wasm/config_parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type Parser struct {

func newParser(confContent []byte) *Parser {
return &Parser{
Config: &Config{Filters: []FilterType{}},
Config: &Config{Decoys: DecoyConfig{}, Session: SessionConfig{}},
ConfigFile: confContent,
}
}
Expand All @@ -51,11 +51,24 @@ func (p *Parser) jsonToStruct(content []byte) error {
if err != nil {
return err
}

filtersJs := json.GetArray("filters")
if json.Exists("session") {
p.Config.Session = SessionConfig{
Session: SessionType{
Key: string(json.GetStringBytes("session", "session", "key")),
In: string(json.GetStringBytes("session", "session", "in")),
Separator: string(json.GetStringBytes("session", "session", "separator")),
},
Username: UsernameType{
In: string(json.GetStringBytes("session", "username", "in")),
Value: string(json.GetStringBytes("session", "username", "value")),
Key: string(json.GetStringBytes("session", "username", "key")),
},
}
}
filtersJs := json.GetArray("decoy", "filters")
for _, filterJs := range filtersJs {
filter := p.filterJsonToStruct(filterJs)
p.Config.Filters = append(p.Config.Filters, *filter)
p.Config.Decoys.Filters = append(p.Config.Decoys.Filters, *filter)
}
return nil
}
Expand Down
14 changes: 13 additions & 1 deletion proxy/wasm/config_parser/type_emptyInstances.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

func EmptyConfig() Config {
return Config{ []FilterType{} }
return Config{ DecoyConfig{}, SessionConfig{} }
}

func EmptyCondition() ConditionType{
Expand All @@ -24,6 +24,18 @@ func EmptyDecoy() DecoyType {
return DecoyType{"", "", "", "", "", ""}
}

func EmptySessionConfig() SessionConfig {
return SessionConfig{ EmptySession(), EmptyUsername() }
}

func EmptySession() SessionType {
return SessionType{"", "", ""}
}

func EmptyUsername() UsernameType {
return UsernameType{"", "", ""}
}

func EmptyInject() InjectType {
return InjectType{ EmptyStore(), EmptyConditions(), EmptyConditions() }
}
Expand Down
27 changes: 24 additions & 3 deletions proxy/wasm/config_parser/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ import (
)

type Config struct {
Decoys DecoyConfig `json:"decoys"`
Session SessionConfig `json:"session"`
}
type SessionConfig struct {
Session SessionType `json:"session"`
Username UsernameType `json:"username"`
}

type SessionType struct {
Key string `json:"key"`
In string `json:"in"`
Separator string `json:"separator"`
}

type UsernameType struct {
In string `json:"in"`
Key string `json:"key"`
Value string `json:"value"`
}

type DecoyConfig struct {
Filters []FilterType `json:"filters"`
}

Expand Down Expand Up @@ -70,7 +91,7 @@ type AlertType struct {
WhenAbsent bool `json:"whenAbsent"`
}

func (c *Config) MakeChecksum() [20]byte{
func (c *DecoyConfig) MakeChecksum() [20]byte{
confStr := ""
for _, filter := range c.Filters {
confStr += filter.Decoy.Key
Expand Down Expand Up @@ -107,7 +128,7 @@ func (c *Config) MakeChecksum() [20]byte{
return sha1.Sum([]byte(confStr))
}

func (c *Config) MakeString() string{
func (c *DecoyConfig) MakeString() string{
confStr := ""
for filterind, filter := range c.Filters {
confStr += fmt.Sprintf("filters[%d].decoy.key: %s \n", filterind, filter.Decoy.Key)
Expand Down Expand Up @@ -149,6 +170,6 @@ func (c *Config) MakeString() string{
return confStr
}

func (c *Config) Print() {
func (c *DecoyConfig) Print() {
fmt.Print(c.MakeString())
}
55 changes: 54 additions & 1 deletion proxy/wasm/config_parser/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (v *validator) printErrors() {
}

func (v *validator) Validate(config *Config) error {
v.validateFilters(config.Filters)
v.validateFilters(config.Decoys.Filters)
v.printErrors()
if len(v.errArr) == 0 {
return nil
Expand All @@ -73,6 +73,41 @@ func (v *validator) validateFilters(filters []FilterType) {
}
}

func (v *validator) validateSessionConf(session SessionConfig) {
v.validateSession(session.Session)
v.validateUsername(session.Username)
}

func (v *validator) validateSession(session SessionType) {
if session == EmptySession() {
return
}
if breaksRequired(session.Key) {
v.addError("session.key ", "can not be empty ")
}
if validInSession(session.In) {
v.addError("session.in", "needs to be cookie or header")
}
}

func (v *validator) validateUsername(username UsernameType) {
if username == EmptyUsername() {
return
}
if breaksRequired(username.Key) && (username.In == "cookie" || username.In == "header") {
v.addError("username.key", "can not be empty for cookier or header")
}
if validInUsername(username.In) {
v.addError("username.in", "needs to be cookie, header or payload")
}
if breaksRequired(username.Value) && username.In == "payload" {
v.addError("username.value", "can not be empty for payload")
}
if invalidRegex(username.Value) {
v.addError("username.dynamicKey", "needs to be a valid regex")
}
}

func (v *validator) validateDecoy(decoy DecoyType) {
v.currentPlace = "filters[" + strconv.Itoa(v.currentFilterInd) + "].decoy"
if decoy == EmptyDecoy() {
Expand Down Expand Up @@ -275,3 +310,21 @@ func invalidRegex(s string) bool {
*/
return false
}

func validInSession(s string) bool {
e := InSession(s)
switch e {
case cookie_inS, header_inS:
return true
}
return false
}

func validInUsername(s string) bool {
e := InUsername(s)
switch e {
case cookie_inU, header_inU, payload_inU:
return true
}
return false
}
31 changes: 23 additions & 8 deletions proxy/wasm/detect/body.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type detectBody struct {
curFilter *config_parser.FilterType
body string
headers map[string]string
cookies map[string]string
request *shared.HttpRequest
}

Expand All @@ -29,14 +30,14 @@ func (d *detectBody) Alert(logParameters map[string]string, headers map[string]s
return nil
}

func OnHttpRequestBody(reqBody string, reqHeaders map[string]string, config *config_parser.Config) error {
d := &detectBody{ config, nil, reqBody, reqHeaders, nil }
func OnHttpRequestBody(reqBody string, reqHeaders map[string]string, cookies map[string]string, config *config_parser.Config) error {
d := &detectBody{ config, nil, reqBody, reqHeaders, cookies, nil }

var err error

if config_proxy.Debug { proxywasm.LogWarn("*** detect request body ***") } //debug
for ind := 0; ind < len(d.conf.Filters); ind++ {
d.curFilter = &d.conf.Filters[ind]
for ind := 0; ind < len(d.conf.Decoys.Filters); ind++ {
d.curFilter = &d.conf.Decoys.Filters[ind]
//proxywasm.LogWarnf("try filter[%v]: %v", ind, d.curFilter) //debug

if isRelReq, err := d.relevantRequest(); err != nil {
Expand All @@ -60,14 +61,14 @@ func OnHttpRequestBody(reqBody string, reqHeaders map[string]string, config *con
return err
}

func OnHttpResponseBody(body string, headers map[string]string, config *config_parser.Config, request *shared.HttpRequest) error {
d := &detectBody{ config, nil, body, headers, request }
func OnHttpResponseBody(body string, headers map[string]string, cookies map[string]string, config *config_parser.Config, request *shared.HttpRequest) error {
d := &detectBody{ config, nil, body, headers, cookies, request }

var err error

if config_proxy.Debug { proxywasm.LogWarn("*** detect reponse body ***") } //debug
for ind := 0; ind < len(d.conf.Filters); ind++ {
d.curFilter = &d.conf.Filters[ind]
for ind := 0; ind < len(d.conf.Decoys.Filters); ind++ {
d.curFilter = &d.conf.Decoys.Filters[ind]
//proxywasm.LogWarnf("try filter[%v]: %v", ind, d.curFilter) //debug

err, skip := d.checkConditionsResponse(ind)
Expand Down Expand Up @@ -177,6 +178,13 @@ func (d *detectBody) detectDecoyInRequest() error {
}
}
if sendAlert {
authenticated, username := FindSession(map[string]map[string]string{"header": d.headers, "cookie": d.cookies, "payload": { "payload": d.body }}, nil, d.conf.Session)
if authenticated != nil {
alertInfos["authenticated"] = *authenticated
}
if username != nil {
alertInfos["username"] = *username
}
err = d.Alert(alertInfos, d.headers)
if err != nil {
return err
Expand Down Expand Up @@ -255,6 +263,13 @@ func (d *detectBody) detectDecoyInResponse() error {
}

if sendAlert {
authenticated, username := FindSession(map[string]map[string]string{ "header": d.request.Headers, "cookie": d.request.Cookies, "payload": { "payload": *d.request.Body }}, &map[string]map[string]string{ "header": d.headers, "cookie": d.cookies, "body": { "body": d.body }}, d.conf.Session)
if authenticated != nil {
alertInfos["authenticated"] = *authenticated
}
if username != nil {
alertInfos["username"] = *username
}
err := d.Alert(alertInfos, d.request.Headers)
if err != nil {
return err
Expand Down
Loading

0 comments on commit a8f4ed4

Please sign in to comment.