3
\$\begingroup\$

I am a fan of f"{strings}" in Python,

However some situations could possibly be dangerous when doing f strings from user input, leaking API keys or even code execution!

So I decided to make something to mitigate these attacks. It works by searching with regex over the input string, and will return an empty string if it detects any evil piece of code.

It uses the fmt package

import fmt as f
import re
import os # This is for checking the system call
import doctest
# Setup
SECRET_GLOBAL = 'this is a secret'
class Error:
 def __init__(self):
 passs
def a_function():
 return SECRET_GLOBAL
# Here is where the code begins
DANGEROUS_CODES = [
 re.compile(r'(\.system\(.*\))'), # Any call to system
 re.compile(r'(__[\w]+__)'), # Any internals
 re.compile(r'([\w\d]+\(\))') # Functions
 ]
def safe_format(st):
 '''
 Safe python f-string formatting
 this will detect evil code from fstring making formatting safe.
 args:
 st (str): The f-string
 returns:
 Empty string, if dangerous code is found
 Executes the fstring normally if no dangerous code is found
 Test globals acces
 >>> safe_format('{Error().__init__.__globals__[SECRET_GLOBAL]}')
 ''
 Test function acces
 >>> safe_format('a_function()')
 ''
 Test code execution via import
 >>> safe_format('{__import__("os").system("dir")}')
 ''
 Test code execution with imported os
 >>> safe_format('{os.system("dir")}')
 ''
 Test no stack trace
 >>> safe_format('{0/0}')
 ''
 Test acceptable fstring
 >>> safe_format('{6 * 6}')
 '36'
 '''
 if any(re.search(danger, st) for danger in DANGEROUS_CODES):
 return ''
 try:
 return f(st)
 except Exception as e:
 return ''
if __name__ == '__main__':
 doctest.testmod()
  • Did i miss anything?
  • Is this approach acceptable?
asked Sep 21, 2018 at 12:19
\$\endgroup\$
4
  • \$\begingroup\$ Wouldn't the safer solution be to not interpolate strings from user input? \$\endgroup\$ Commented Sep 21, 2018 at 14:46
  • \$\begingroup\$ @MartinR Yes... I get your point, but that is not the goal of this post. If there would ever be a need, I want to know if this is a possible way of mitigating attack vectors \$\endgroup\$ Commented Sep 21, 2018 at 14:49
  • \$\begingroup\$ Hi Ludisposed, can you add some test strings so we can validate your assumptions? assert val is True, assert val is False etc. \$\endgroup\$ Commented Sep 25, 2018 at 6:02
  • \$\begingroup\$ @C.Harley There are some doctests in there, however as stefan correctly pointed out, they do not cover all testcases \$\endgroup\$ Commented Sep 26, 2018 at 7:50

1 Answer 1

5
\$\begingroup\$

If you forbid function calls you cannot use getters which then is a big restriction. If you allow them it is hard to get it safe. Also your regexes will have collateral damages and filter legal expressions. So I think your approach is not so good. Don't allow users to mess with format strings. Use Template from string for that purpose.

to your regexes:

  • if you have a re for function you do not need one for system
  • [\w] includes [\d]
  • your re function catches only calls without arguments
  • re function and re system do not care about whitespaces and are vulnerable
  • all regexes do collateral damage when matching outside the format braces

here are some more test that currently fail

 '''
 some attacks
 >>> safe_format('a_function(666)')
 ''
 >>> safe_format('a_function ()')
 ''
 >>> safe_format('{os. system("dir")}')
 ''
 some collateral damage
 >>> safe_format('...system(left) {5}')
 '...system(left) 5'
 >>> safe_format('f() gives {5}')
 'f(g) gives 5'
 '''
answered Sep 21, 2018 at 17:56
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.