- Home
- >> Answer Tech Queries
- >> JAVA
-
Help with java defensive programming technique
over 7 years ago
I have a the following four classes..
FTPApplication- Models FTP commands in a modular, extendible way. This abstract class provides
several methods used by both the client and server.
ServerSession- Models an FTP session with a client. Implements methods declared by FTPApplication
from the perspective of an FTP server.
FTPServer- A pseudo FTP server. Accepts client connection and initiates a session.
FTPClient- A pseudo-FTP client. Connects to server and enters a control loop which enables it to
query the server.I want to make appropriate changes to harden the software system against attack and misuse. How can I do that and what changes do I need to make?
This code is working correctly but I want to make it more secure and harden it for misuse.import java.io.*; import java.net.Socket; import java.text.MessageFormat; import java.util.StringTokenizer; /** * Models FTP commands in a modular, extendible way. This abstract class * provides several methods used by both the client and server. * abstract public class FTPApplication { /* constants common to the client and server */ static final int SOCKET_TIMEOUT = 300000; static final int ERROR = -1; static final String DEFAULT_FILEBASE = "user.dir"; /* FTP commands */ static final String PUT = "put"; static final String GET = "get"; static final String LS = "ls"; static final String EXIT = "exit"; /* Path to the directory from which to serve or store files */ protected String fileBase; /* Network I/O objects used for sending/receiving data */ protected Socket socket; protected BufferedReader lineIn; protected PrintWriter lineOut; /* Flag used to break session loop */ protected boolean exitRecieved = false; // ---------------------------------------------------------------------- // CONSTRUCTOR // ---------------------------------------------------------------------- /** * Default constructor. Establishes file base. * * @param filebase * The directory from which to read & write files. */ public FTPApplication(String filebase) { try { File path = new File(filebase); this.fileBase = path.getCanonicalPath() + File.separator; } catch (IOException ioe) { this.fileBase = ""; System.err.printf("Could not access %s: %s", filebase, ioe); } } // ---------------------------------------------------------------------- // PROTECTED METHODS // ---------------------------------------------------------------------- /** * Sets a timeout on the socket and instantiates I/O objects. * * @throws IOException */ protected void setUpIO() throws IOException { if (socket != null) { socket.setSoTimeout(SOCKET_TIMEOUT); InputStreamReader input = new InputStreamReader( socket.getInputStream()); lineIn = new BufferedReader(input); lineOut = new PrintWriter(socket.getOutputStream(), true); } else { System.err.println("Cannot set up IO, socket is null.\n"); } } /** * Closes the socket and its associated I/O objects. */ protected void terminate() { try { System.out.printf("Terminating session ... "); if (lineIn != null) { lineIn.close(); } if (lineOut != null) { lineOut.flush(); lineOut.close(); } if (socket != null) { socket.close(); } System.out.printf("done.\n"); } catch (IOException ioe) { System.err.printf("I/O Error terminating session: %s\n", ioe); } } /** * Calls the proper method based on user input. * * @param args * User arguments */ protected void processCommand(StringTokenizer args) { if (args.hasMoreTokens()) { String command = args.nextToken(); if (command.equalsIgnoreCase(PUT)) { String file = args.hasMoreTokens() ? args.nextToken() : null; handlePut(file); } else if (command.equalsIgnoreCase(GET)) { String file = args.hasMoreTokens() ? args.nextToken() : null; handleGet(file); } else if (command.equalsIgnoreCase(LS)) { handleLs(); } else if (command.equalsIgnoreCase(EXIT)) { handleExit(); } else { handleOther(true); } } else { handleOther(false); } } /** * Returns the path to the given file by prepending the filebase. * * @param file * target filename * @return full local path to file */ protected String getFilePath(String file) { return MessageFormat.format("{0}{1}", fileBase, file); } /** * Read the given file from disk into a byte array. * * @param file * the target file * @return byte array containing file contents */ protected byte[] readFile(File file) { byte[] data = new byte[(int) file.length()]; try { System.out.printf("Loading %s ... ", file); FileInputStream fis = new FileInputStream(file); int amt = fis.read(data, 0, data.length); fis.close(); System.out.printf("done (%d bytes).\n", amt); } catch (IOException ioe) { System.err.printf("Error reading file: %s\n", ioe); } return data; } /** * Writes a file to the filebase. * * @param data * Raw bytes of a file * @param filename * the name to give the new file. * @throws IOException */ protected void storeFile(byte[] data, String filename) throws IOException { String toWrite = getFilePath(filename); System.out.printf("Storing file at %s... ", toWrite); FileOutputStream fileOut = new FileOutputStream(toWrite); fileOut.write(data); fileOut.flush(); fileOut.close(); System.out.printf("done.\n"); } /** * Receives a byte[] as individual ints. * * @return a byte[] value */ protected byte[] receiveData() throws IOException { // get amount of bytes expected int byteAmt = new Integer(lineIn.readLine()); byte[] toReturn = new byte[byteAmt]; // receive the bytes for (int i = 0; i < byteAmt; i++) { toReturn[i] = new Integer(lineIn.readLine()).byteValue(); } return toReturn; } /** * Transmits bytes one by one as ints. * * @param bytes * a byte[] value */ protected void sendData(byte[] bytes) { // tell receiver # of bytes to expect lineOut.println(bytes.length); // send each byte as ints, one-by-one for (byte aB : bytes) { lineOut.println(new Byte(aB).intValue()); } } /** * Sends the string to over the socket. * * @param message */ protected void sendMessage(String message) { sendData(message.getBytes()); } /** * Receives a string from the socket. * * @return string received */ protected String receiveMessage() throws IOException { return new String(receiveData()); } // ---------------------------------------------------------------------- // ABSTRACT METHODS - inherited & implemented by client and server // ---------------------------------------------------------------------- /** * Uploads a client-side file to the server. * * @param filename */ abstract protected void handlePut(String filename); /** * Downloads a file from the server * * @param filename */ abstract protected void handleGet(String filename); /** * Lists the files available on the server. */ abstract protected void handleLs(); /** * Ends an FTP session, tearing down the server-client connection. */ abstract protected void handleExit(); /** * Models receiving bad input. * * @param invalidCmd */ abstract protected void handleOther(boolean invalidCmd); } import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import java.text.MessageFormat; import java.util.StringTokenizer; /** * A pseudo-FTP client. Connects to server and enters a control loop which * enables it to query the server. public class FTPClient extends FTPApplication { protected int port; protected InetAddress ip; // ---------------------------------------------------------------------- // CONSTRUCTOR // ---------------------------------------------------------------------- /** * Default constructor. * * @param port * The port to connect to on the server. * @param ip * The server's IP address * @param fileBase * The directory from which the client will read and write. */ public FTPClient(int port, InetAddress ip, String fileBase) { super(fileBase); this.port = port; this.ip = ip; System.out.printf("Working out of %s\n", this.fileBase); } // ---------------------------------------------------------------------- // PRIVATE METHODS // ---------------------------------------------------------------------- /** * Prints usage message to the console. */ private static void printUsage() { System.out.printf("Usage: FTPClient -i <ip> -p <port> [options]\n\n"); System.out.printf("\tWhere valid options include: \n"); System.out.printf("\t\t -h \t Prints usage \n"); System.out.printf("\t\t -d \t File directory \n"); } // ---------------------------------------------------------------------- // INHERITED, PROTECTED METHODS // ---------------------------------------------------------------------- /** * Checks that the specified file is valid, then transmits the file's name, * size, and data. After transmission, the client waits for a response from * the server. * * @param filename * Name of the file to send. */ protected void handlePut(String filename) { // confirm existence of file File file = new File(getFilePath(filename)); if (!file.exists()) { System.err.printf("%s does not exist.\n", file); handleOther(false); } else { try { // send filename System.out.printf("Sending filename ... "); sendMessage(MessageFormat.format("{0} {1}", PUT, filename)); System.out.printf("done.\n"); // send data byte[] data = readFile(file); sendData(data); // await reply System.out.printf("Server reply: "); String reply = receiveMessage(); System.out.printf("%s\n", reply); } catch (IOException ioe) { ioe.printStackTrace(); } } } /** * Sends the request to the server, receives the length of the file (if it * exists), and receives the file's data. * * @param filename * Name of the file to receive. */ protected void handleGet(String filename) { try { // request file sendMessage(MessageFormat.format("{0} {1}", GET, filename)); // receive file length System.out.printf("Receiving length ... "); String input = receiveMessage(); long length = (input != null) ? Long.valueOf(input) : ERROR; System.out.printf("done.\n"); // in case the file doesn't exist on the server side if (length == ERROR) { System.out.printf("Server: %s does not exist.\n", filename); } else { // GET DATA byte[] data = receiveData(); // SAVE DATA storeFile(data, filename); System.out.printf("Received %s OK.\n", filename); } } catch (IOException e) { e.printStackTrace(); } } /** * Sends the listing request to the server, receives the amount of files * there are, and receives each filename. */ protected void handleLs() { try { // request listing sendMessage(LS); // receive number of files String input = receiveMessage(); int fileAmt = input != null ? Integer.valueOf(input) : ERROR; // receive and print names of files if (fileAmt != ERROR) { for (int i = 0; i < fileAmt; i++) { System.out.printf("\t%s\n", receiveMessage()); } } } catch (IOException e) { e.printStackTrace(); } } /** * Models receiving a null or invalid command * * @param invalidCmd * If true, prints message stating that an invalid cmd was * received. */ protected void handleOther(boolean invalidCmd) { sendMessage("\n"); if (invalidCmd) { System.err.printf("Invalid command.\n"); } } /** * Sends an exit signal to the server and terminates the connection. */ protected void handleExit() { sendMessage(EXIT); exitRecieved = true; terminate(); } // ---------------------------------------------------------------------- // PUBLIC METHODS // ---------------------------------------------------------------------- /** * Establish connection with server and create I/O objects. * * @return true if connection was established successfully. */ public boolean connect() { boolean success = false; try { System.out.printf("Connecting to %s:%d ... ", ip, port); socket = new Socket(ip, port); setUpIO(); success = true; System.out.printf("Established.\n"); } catch (Exception e) { e.printStackTrace(); } return success; } /** * Begins loop that allows client to send commands to the server. */ public void queryServer() { if (lineIn == null || lineOut == null) { System.err.printf("Cannot query server, IO has not been set up\n"); } else { try { String serverReply; String userInput; StringTokenizer args; InputStreamReader input = new InputStreamReader(System.in); BufferedReader console = new BufferedReader(input); // client-side control loop while (!exitRecieved) { // Wait for PROMPT serverReply = receiveMessage(); System.out.printf("%s", serverReply); // ACCEPT & PROCESS USER INPUT userInput = console.readLine(); userInput = userInput == null ? "" : userInput; args = new StringTokenizer(userInput); processCommand(args); } // clean up console.close(); input.close(); } catch (Exception e) { e.printStackTrace(); } } } /** * Driver for FTPClient * * @param args * Command-line arguments */ public static void main(String[] args) { int port = -1; String directory = System.getProperty(FTPApplication.DEFAULT_FILEBASE); InetAddress ip = null; // Process arguments boolean helpRequested = false; for (int index = 0; !helpRequested && index < args.length; index++) { if (args[index].length() == 2 & args[index].charAt(0) == '-') { switch (args[index].charAt(1)) { case 'p': if (index + 1 >= args.length) { System.err.println("Port number expected."); helpRequested = true; } else { port = Integer.valueOf(args[++index]); } break; case 'h': helpRequested = true; break; case 'd': if (index + 1 >= args.length) { System.err.println("Directory expected."); helpRequested = true; } else { File path = new File(args[++index]); if (path.exists()) { directory = args[index]; } else { System.err.println("Invalid directory"); helpRequested = true; } } break; case 'i': if (index + 1 >= args.length) { System.err.println("IP address expected."); helpRequested = true; } else { try { ip = InetAddress.getByName(args[++index]); } catch (UnknownHostException uhe) { System.err.printf("Bad address: %s\n", uhe); helpRequested = true; } } break; default: helpRequested = true; break; } } else { helpRequested = true; } } // Begin execution if (helpRequested || port == -1 || ip == null) { printUsage(); } else { FTPClient client = new FTPClient(port, ip, directory); if (client.connect()) { client.queryServer(); } } } } import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /** * A pseudo FTP server. Accepts client connection and initiates a session. public class FTPServer { protected int port; protected String fileBase; // ---------------------------------------------------------------------- // CONSTRUCTOR // ---------------------------------------------------------------------- /** * Default constructor. * * @param port * Port which the server will listen on. * @param fileBase * The path to the directory from which files will be served */ public FTPServer(int port, String fileBase) { this.port = port; this.fileBase = fileBase; } // ---------------------------------------------------------------------- // PRIVATE METHODS // ---------------------------------------------------------------------- /** * Prints usage message to the console. */ private static void printUsage() { System.out.printf("Usage: FTPServer -p <port> [options] \n\n"); System.out.printf("\tWhere valid options include: \n"); System.out.printf("\t\t -h \t Prints usage \n"); System.out.printf("\t\t -d \t File directory \n"); } // ---------------------------------------------------------------------- // PUBLIC METHODS // ---------------------------------------------------------------------- /** * Performs a passive open and accepts a client. ServerSession objects are * instantiated to serve client requests. */ public void acceptClients() { try { // initiate server socket System.out.printf("Creating socket ... "); ServerSocket serverSocket = new ServerSocket(port); serverSocket.setSoTimeout(FTPApplication.SOCKET_TIMEOUT); System.out.printf("Bound to %s:%s \n", InetAddress.getLocalHost(), serverSocket.getLocalPort()); // listen for clients System.out.printf("Listening for connections ... "); Socket clientSocket = serverSocket.accept(); System.out.printf("Accepted client from %s\n", clientSocket); // SERVE THE CLIENT ServerSession session = new ServerSession(clientSocket, fileBase); session.serveClient(); // clean up System.out.printf("Closing socket ... "); serverSocket.close(); System.out.printf("done."); } catch (IOException e) { e.printStackTrace(); } } /** * Driver for the FTPServer. * * @param args * command line arguments */ public static void main(String[] args) { int port = -1; String directory = System.getProperty(FTPApplication.DEFAULT_FILEBASE); // Process arguments boolean helpRequested = false; for (int index = 0; !helpRequested & index < args.length; index++) { if (args[index].length() == 2 & args[index].charAt(0) == '-') { switch (args[index].charAt(1)) { case 'p': if (index + 1 >= args.length) { System.err.println("Port number expected."); helpRequested = true; } else { port = Integer.valueOf(args[++index]); } break; case 'h': helpRequested = true; break; case 'd': if (index + 1 >= args.length) { System.err.println("Directory expected."); helpRequested = true; } else { File path = new File(args[++index]); if (path.exists()) { directory = args[index]; } else { System.err.println("Invalid directory"); helpRequested = true; } } break; default: helpRequested = true; break; } } else { helpRequested = true; } } // Begin execution if (helpRequested || port == -1) { printUsage(); } else { FTPServer server = new FTPServer(port, directory); server.acceptClients(); } } } import java.io.File; import java.io.IOException; import java.net.Socket; import java.util.StringTokenizer; public class ServerSession extends FTPApplication { private static final String PROMPT = "secFTP>"; // ---------------------------------------------------------------------- // CONSTRUCTOR // ---------------------------------------------------------------------- /** * Default constructor. Sets up I/O objects associated with the socket. * * @param socket * Network endpoint connected to a client. * @param filePath * Path to the file base. */ public ServerSession(Socket socket, String filePath) throws IOException { super(filePath); System.out.printf("Serving files out of %s. \n", fileBase); this.socket = socket; setUpIO(); } // ---------------------------------------------------------------------- // PROTECTED METHODS // ---------------------------------------------------------------------- /** * Receives the name, length, and data of the file to upload. The data * received is written to a file and a status message is sent. * * @param filename * the name of the file to upload. */ protected void handlePut(String filename) { boolean success = false; try { System.out.printf("Receiving %s\n", filename); // GET DATA byte[] data = receiveData(); // SAVE DATA if (data != null) { storeFile(data, filename); success = true; } } catch (IOException ioe) { System.err.printf("I/O error receiving file: %s\n", ioe); } catch (NumberFormatException nfe) { System.err.printf("Invalid length specified: %s\n", nfe); } // SEND REPLY System.out.printf("Sending reply ... "); String message = success ? "PUT OK" : "PUT FAILED"; sendMessage(message); System.out.printf("done.\n"); } /** * Determines if the specified file exists, and sends the file's length and * data. * * @param filename * name of the file to transmit. */ protected void handleGet(String filename) { System.out.printf("Preparing to send %s ... \n", filename); // CHECK EXISTANCE OF FILE, AND SEND LENGTH File file = new File(getFilePath(filename)); if (!file.exists()) { System.err.printf("%s does not exist.\n", file); sendMessage(String.valueOf(ERROR)); } else { // send length System.out.printf("Sending length ... "); sendMessage(String.valueOf(file.length())); System.out.printf("done.\n"); // send data byte[] data = readFile(file); sendData(data); } } /** * Determines the available files, then sends the number, followed by names * of them. */ protected void handleLs() { System.out.printf("Listing available files.\n"); File[] availableFiles = new File(fileBase).listFiles(); if (availableFiles == null) { System.err.printf("%s is not a directory.\n", fileBase); sendMessage(String.valueOf(ERROR)); } else { sendMessage(String.valueOf(availableFiles.length)); /* send each file name */ for (File file : availableFiles) { sendMessage(file.getName()); } } } /** * Models receiving an invalid or null command: ignore it. * * @param invalidCmd */ protected void handleOther(boolean invalidCmd) { // do nothing } /** * Terminates connection to client, toggles control loop variable. */ protected void handleExit() { exitRecieved = true; terminate(); } // ---------------------------------------------------------------------- // PUBLIC METHODS // ---------------------------------------------------------------------- /** * Initiates the service loop that serves client requests. */ public void serveClient() { if (lineIn == null || lineOut == null) { System.err.printf("I/O has not been set up.\n"); } else { try { String clientRequest; StringTokenizer args; // control loop, receiving client requests while (!exitRecieved) { // PRESENT PROMPT sendMessage(PROMPT); // ACCEPT & PROCESS INPUT clientRequest = receiveMessage(); clientRequest = clientRequest == null ? "" : clientRequest; args = new StringTokenizer(clientRequest); processCommand(args); } } catch (IOException ioe) { System.err.printf("IO Error receiving client input: %s\n", ioe); } } } }
I need to make sure that:
* Ensure that any user-supplied information is verified as safe before it is acted upon.
* Harden appropriate classes and/or members against improper data access, extension, or modification.
* Review the exception handling throughout, and ensure that each exception is properly reported
and cleanly recovered from.
0 Answer(s)