@@ -14,55 +14,8 @@ The `noncompliant01.py` code example demonstrates the Thread-Per-Message design
14
14
15
15
* [ noncompliant01.py] ( noncompliant01.py ) :*
16
16
17
- ``` py
18
- """ Non-compliant Code Example """
19
- import logging
20
- import threading
21
- import time
22
-
23
- logging.basicConfig(level = logging.INFO )
24
-
25
-
26
- def process_message (message : str , processed_messages : list ):
27
- """ Method simulating mediation layer i/o heavy work"""
28
- logging.debug(" process_message: started message %s working %i s" , message, int (message) / 10 )
29
- for _ in range (int (message)):
30
- time.sleep(0.01 )
31
- logging.debug(" process_message: completed message %s " , message)
32
- processed_messages.append(f " processed { message} " )
33
-
34
-
35
- class MessageAPI (object ):
36
- """ Class simulating the front end facing API"""
37
-
38
- def add_messages (self , messages : list ) -> list :
39
- """ Receives a list of messages to work on """
40
- logging.info(" add_messages: got %i messages to process" , len (messages))
41
- processed_messages = []
42
- threads = []
43
- for message in messages:
44
- threads.append(
45
- threading.Thread(target = process_message, args = [message, processed_messages]))
46
- threads[- 1 ].start()
47
- logging.debug(" add_messages: submitted %i messages" , len (messages))
48
- for thread in threads:
49
- thread.join()
50
- logging.info(" add_messages: messages_done=%i " , len (processed_messages))
51
- return processed_messages
52
-
53
-
54
- # ####################
55
- # exploiting above code example
56
- # ####################
57
- mapi = MessageAPI()
58
- attacker_messages = [str (msg) for msg in range (1000 )]
59
- print (" ATTACKER: start sending messages" )
60
- result_list = mapi.add_messages(attacker_messages)
61
- print (
62
- f " ATTACKER: done sending { len (attacker_messages)} messages, got { len (result_list)} messages "
63
- f " back " )
64
- print (f " ATTACKER: result_list = { result_list} " )
65
-
17
+ ``` python
18
+ {% include_relative noncompliant01.py % }
66
19
```
67
20
68
21
The ` noncompliant01.py ` code creates a risk of having a single component exhaust all available hardware resources even when used by a single user for a single use-case. When running the code, the Unix ` top ` command can show a significant increase in CPU usage (%CPU exceeds 100% because multiple cores are being used):
@@ -80,64 +33,8 @@ The attacker could still flood the server by creating multiple MessageAPI, each
80
33
81
34
* [ compliant01.py] ( compliant01.py ) *
82
35
83
- ``` py
84
- """ Compliant Code Example """
85
- import logging
86
- import time
87
- from concurrent.futures import ThreadPoolExecutor
88
- from concurrent.futures import wait
89
-
90
- logging.basicConfig(level = logging.INFO )
91
-
92
-
93
- def process_message (message : str ):
94
- """ Method simulating mediation layer i/o heavy work"""
95
- logging.debug(" process_message: started message %s working %i s" , message, int (message) / 10 )
96
- for _ in range (int (message)):
97
- time.sleep(0.01 )
98
- logging.debug(" process_message: completed message %s " , message)
99
- return f " processed { message} "
100
-
101
-
102
- class MessageAPI (object ):
103
- """ Class simulating the front end facing API"""
104
- # TODO : Prevent the attacker from creating multiple MessageAPI objects
105
-
106
- def __init__ (self ):
107
- # TODO : set or handle timeout as it is provided by the mediation layer
108
- self .timeout = 1
109
- self .executor = ThreadPoolExecutor()
110
-
111
- def add_messages (self , messages : list ) -> list :
112
- """ Receives a list of messages to work on """
113
- # TODO : limit on max messages from the mediation layer.
114
- # TODO : input sanitation.
115
- futures = []
116
- # with self.executor:
117
- for message in messages:
118
- futures.append(self .executor.submit(process_message, message))
119
- logging.debug(" add_messages: submitted %i messages, waiting for %i s to complete." , len (messages), self .timeout)
120
- messages_done, messages_not_done = wait(futures, timeout = self .timeout)
121
- for future in messages_not_done:
122
- future.cancel()
123
-
124
- logging.info(" add_messages: messages_done=%i messages_not_done=%i " , len (messages_done), len (messages_not_done))
125
- process_messages = []
126
- for future in messages_done:
127
- process_messages.append(future.result())
128
- return process_messages
129
-
130
-
131
- # ####################
132
- # exploiting above code example
133
- # ####################
134
- mapi = MessageAPI()
135
- result_list = []
136
- attacker_messages = [str (msg) for msg in range (100 )]
137
- print (" ATTACKER: start sending messages" )
138
- result_list = mapi.add_messages(attacker_messages)
139
- print (f " ATTACKER: done sending { len (attacker_messages)} messages, got { len (result_list)} messages back " )
140
- print (f " ATTACKER: result_list = { result_list} " )
36
+ ``` python
37
+ {% include_relative compliant01.py % }
141
38
```
142
39
143
40
Now, after the timeout is reached, ` MessageAPI ` drops unprocessed messages and returns partial results:
@@ -155,63 +52,8 @@ The `executor.shutdown()` method has a `cancel_futures` parameter, which by def
155
52
156
53
* [ noncompliant02.py] ( noncompliant02.py ) :*
157
54
158
- ``` py
159
- """ Non-compliant Code Example """
160
- import logging
161
- import time
162
- from concurrent.futures import ThreadPoolExecutor
163
- from concurrent.futures import wait
164
-
165
- logging.basicConfig(level = logging.INFO )
166
-
167
-
168
- def process_message (message : str ):
169
- """ Method simulating mediation layer i/o heavy work"""
170
- logging.debug(" process_message: started message %s working %i s" , message, int (message) / 10 )
171
- for _ in range (int (message)):
172
- time.sleep(0.01 )
173
- logging.debug(" process_message: completed message %s " , message)
174
- return f " processed { message} "
175
-
176
-
177
- class MessageAPI (object ):
178
- """ Class simulating the front end facing API"""
179
-
180
- def __init__ (self ):
181
- self .executor = ThreadPoolExecutor()
182
- self .timeout = 1
183
-
184
- def add_messages (self , messages : list ) -> list :
185
- """ Receives a list of messages to work on """
186
- futures = []
187
- for message in messages:
188
- futures.append(self .executor.submit(process_message, message))
189
-
190
- logging.debug(" add_messages: submitted %i messages, waiting for %i s to complete." ,
191
- len (messages), self .timeout)
192
- messages_done, messages_not_done = wait(futures, timeout = self .timeout)
193
-
194
- logging.info(" add_messages: messages_done=%i messages_not_done=%i " , len (messages_done),
195
- len (messages_not_done))
196
-
197
- process_messages = []
198
- for future in messages_done:
199
- process_messages.append(future.result())
200
- return process_messages
201
-
202
-
203
- # ####################
204
- # exploiting above code example
205
- # ####################
206
- mapi = MessageAPI()
207
- result_list = []
208
- attacker_messages = [str (msg) for msg in range (1000 )]
209
- print (" ATTACKER: start sending messages" )
210
- result_list = mapi.add_messages(attacker_messages)
211
- print (
212
- f " ATTACKER: done sending { len (attacker_messages)} messages, got { len (result_list)} messages "
213
- f " back " )
214
- print (f " ATTACKER: result_list = { result_list} " )
55
+ ``` python
56
+ {% include_relative noncompliant02.py % }
215
57
```
216
58
217
59
## Compliant Solution (Thread Pool with grace period)
@@ -220,77 +62,8 @@ The `compliant01.py` can be expanded by adding a grace period. Before dropping t
220
62
221
63
* [ compliant02.py] ( compliant02.py ) :*
222
64
223
- ``` py
224
- """ Compliant Code Example """
225
- import logging
226
- import time
227
- from concurrent.futures import ThreadPoolExecutor
228
- from concurrent.futures import wait
229
-
230
- logging.basicConfig(level = logging.INFO )
231
-
232
-
233
- def process_message (message : str ):
234
- """ Method simulating mediation layer i/o heavy work"""
235
- logging.debug(" process_message: started message %s working %i s" , message, int (message) / 10 )
236
- for _ in range (int (message)):
237
- time.sleep(0.01 )
238
- logging.debug(" process_message: completed message %s " , message)
239
- return f " processed { message} "
240
-
241
-
242
- class MessageAPI (object ):
243
- """ Class simulating the front end facing API"""
244
- # TODO : Prevent the attacker from creating multiple MessageAPI objects
245
-
246
- def __init__ (self ):
247
- # TODO : set or handle timeout as it is provided by the mediation layer
248
- self .timeout = 1
249
- self .executor = ThreadPoolExecutor()
250
-
251
- def add_messages (self , messages : list ) -> list :
252
- """ Receives a list of messages to work on """
253
- # TODO : limit on max messages from the mediation layer.
254
- # TODO : input sanitation.
255
- futures = []
256
- # with self.executor:
257
- for message in messages:
258
- futures.append(self .executor.submit(process_message, message))
259
- logging.debug(" add_messages: submitted %i messages, waiting for %i s to complete." ,
260
- len (messages), self .timeout)
261
- messages_done, messages_not_done = wait(futures, timeout = self .timeout)
262
- logging.info(" add_messages: messages_done=%i messages_not_done=%i " , len (messages_done),
263
- len (messages_not_done))
264
- if len (messages_not_done) > 0 :
265
- # TODO : be graceful, warn a trusted client
266
- logging.warning(" add_messages: %i messages taking longer than %i s, %i more to process" ,
267
- len (messages), self .timeout, len (messages_not_done))
268
- messages_done, messages_not_done = wait(futures, timeout = self .timeout)
269
- logging.info(" add_messages: messages_done=%i messages_not_done=%i " , len (messages_done),
270
- len (messages_not_done))
271
- for future in messages_not_done:
272
- future.cancel()
273
-
274
- logging.info(" add_messages: messages_done=%i messages_not_done=%i " , len (messages_done),
275
- len (messages_not_done))
276
- process_messages = []
277
- for future in messages_done:
278
- process_messages.append(future.result())
279
- return process_messages
280
-
281
-
282
- # ####################
283
- # exploiting above code example
284
- # ####################
285
- mapi = MessageAPI()
286
- result_list = []
287
- attacker_messages = [str (msg) for msg in range (100 )]
288
- print (" ATTACKER: start sending messages" )
289
- result_list = mapi.add_messages(attacker_messages)
290
- print (
291
- f " ATTACKER: done sending { len (attacker_messages)} messages, got { len (result_list)} messages "
292
- f " back " )
293
- print (f " ATTACKER: result_list = { result_list} " )
65
+ ``` python
66
+ {% include_relative compliant02.py % }
294
67
```
295
68
296
69
## Automated Detection
0 commit comments